Dupliquer des données en base de donnée

a marqué ce sujet comme résolu.

Hello à tous,
Je suis en train de me prendre la tête sur mon projet actuel. C’est une application qui permet de gérer une ICD et ses versions. En gros l’utilisateur rentre les différents équipements qui composent le réseau avec ses données (adresse IP, MAC…) ainsi que les messages échangés entre ces équipements et il peut générer la documentation ou des fichiers de configuration automatiquement.

Ça, ça fonctionne. Maintenant il faut pouvoir gérer des versions de cette ICD. Tous les modèles héritent d’un VersionnedModel:

class VersionManager(models.Manager):
     def get_queryset(self):
        return super().get_queryset().filter(version__id=get_current_version_id())


class VersionnedModel(BaseModel):
    version = models.ForeignKey(Version, on_delete=models.CASCADE)
    versionned_objects = VersionManager()
    objects = models.Manager()
    
    class Meta:
        abstract = True
        
    def save(self, *args, **kwargs):
        if self.version.frozen:
            raise ValueError("Cannot modify a frozen version")
        
        if not self.version:
            self.version = get_current_version()
        super(VersionnedModel, self).save(*args, **kwargs)
class Version(BaseModel):
    edition = models.PositiveIntegerField(unique=True, validators=[MinValueValidator(1)])
    description = models.TextField(blank=True, null=True, default=None)
    frozen = models.BooleanField(default=False)
    
    def __str__(self):
        return str(self.edition)

Quand l’utilisateur veut créer une nouvelle version, il a la possibilité de le faire à partir d’une ancienne version et donc de dupliquer l’entièreté des données d’une version précédente vers la nouvelle version. Et là vient le problème : comment dupliquer toutes les données liées à une version tout en gérant les relations multiples entre les modèles ? (Dont des relations qui partent et pointent vers un même modèle !).

Voici un exemple de modèles:

class InetAddress(VersionnedModel):
    value = models.GenericIPAddressField()
    
    objects = models.Manager()
    
    class Meta:
        unique_together = [('value', 'version')]
    
    def __str__(self):
        return str(self.value)

class Device(VersionnedModel):
    name = models.CharField(max_length=255)
    mac_address = models.CharField(max_length=17, validators=[validators.validate_unique_mac_address])
    inet_address = models.OneToOneField(InetAddress, on_delete=models.CASCADE)
    
    objects = models.Manager()

    class Meta:
        unique_together = [('name', 'version'), ('mac_address', 'version')]

    def __str__(self):
        return self.name

class PhysicalPort(VersionnedModel):
    index = models.PositiveIntegerField(validators=[MinValueValidator(1)])
    device = models.ForeignKey(Device, on_delete=models.CASCADE, related_name='ports')
    connected_to = models.OneToOneField('self', blank=True, null=True, default=None, on_delete=models.SET_NULL)
    
    tag_vlan = models.ForeignKey('VLAN', related_name='tagged_ports', blank=True, null=True, default=None, on_delete=models.SET_NULL)
    
    objects = models.Manager()
    
    class Meta:
        unique_together = [('index', 'device')]
    
    def __str__(self):
        return f'{self.device}:{self.index}'

class VLAN(VersionnedModel):
    name = models.CharField(max_length=255)
    tag = models.PositiveIntegerField()

    devices = models.ManyToManyField(Device, blank=True, related_name='vlans')

    objects = models.Manager()

    class Meta:
        verbose_name = 'VLAN'
        verbose_name_plural = 'VLANs'
        unique_together = [('name', 'version'), ('tag', 'version')]
    
    def __str__(self):
        return self.name

class DestinationGroup(VersionnedModel):
    name = models.CharField(max_length=255)
    inet_address = models.OneToOneField(InetAddress, on_delete=models.CASCADE, related_name='destination_group')
    
    vlan = models.ForeignKey(VLAN, on_delete=models.CASCADE, related_name='destination_groups')
    members = models.ManyToManyField(Device, blank=True, related_name='destination_groups')
    
    objects = models.Manager()
    
    class Meta:
        unique_together = [('name', 'version')]
    
    def __str__(self):
        return self.name

Il y en a d’autre dont des modèles avec des relations polymorphiques… (pour gérer les messages et les différents types de champs qu’un message peut avoir).

Quelle serait la bonne approche à ce problème ? En gros dupliquer toutes les données, mettre à jour la version et les differents ID des relations vers celles créées.

Merci pour vos conseils!

+0 -0

Salut,

Je dirais que ça dépend du type des relations.

  • Si la version possède une foreign key vers un modèle particulier, celle-ci peut persister en l’état dans la nouvelle version (les deux versions référencent la même instance).
  • Idem s’il s’agit d’une relation many-to-many entre la version et les modèles.
  • Si c’est une relation one-to-one entre la version et un modèle, il faudrait dupliquer le modèle.
  • Si la version est référencée par une foreign key d’un modèle je dirais que c’est du cas par cas : est-ce que ce modèle doit pointer sur la dernière version crée ou rester assigné à une version en particulier.

Par contre je trouve que ce qui complique les choses c’est que selon ton modèle les versions ne sont pas toujours figées, ça risque de poser problème si tu modifies les données relatives à une ancienne version et que ça affecte une version plus récente. Pour moi la version devrait toujours être figé et chaque modification devrait amener à une nouvelle version.

Et concernant les relations, je ne sais pas exactement comment c’est fait actuellement mais je dirais qu’un modèle version (figé) devrait n’avoir que des relations pointant sur d’autres modèles version (figés).

En espérant que ce message te permette de détricoter ton souci de duplications.

Hello,

Par contre je trouve que ce qui complique les choses c’est que selon ton modèle les versions ne sont pas toujours figées, ça risque de poser problème si tu modifies les données relatives à une ancienne version et que ça affecte une version plus récente. Pour moi la version devrait toujours être figé et chaque modification devrait amener à une nouvelle version.

entwanne

Eh oui c’est le problème c’est qu’on doit pouvoir revenir sur une version passée pour adapter des champs (c’est spécial je sais). D’où mon désir de absolument tout dupliquer, même les relations.

Je pense ne pas avoir le choix que de faire un code bien bourrin, notamment pour les PhysicalPorts. Ils ont ce champ « connected_to » qui pointe vers lui même et donc complique tout ahah.

Merci pour tes idées.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte