Duplicate Django Model Instance and All Foreign Keys Pointing to It

后端 未结 4 463
自闭症患者
自闭症患者 2021-01-02 06:03

I want to create a method on a Django model, call it model.duplicate(), that duplicates the model instance, including all the foreign keys pointing to it. I kno

4条回答
  •  天涯浪人
    2021-01-02 06:21

    Although I accepted the other poster's answer (since it helped me get here), I wanted to post the solution I ended up with in case it helps someone else stuck in the same place.

    def duplicate(self):
        """
        Duplicate a model instance, making copies of all foreign keys pointing
        to it. This is an in-place method in the sense that the record the
        instance is pointing to will change once the method has run. The old
        record is still accessible but must be retrieved again from
        the database.
        """
        # I had a known set of related objects I wanted to carry over, so I
        # listed them explicitly rather than looping over obj._meta.fields
        fks_to_copy = list(self.fkeys_a.all()) + list(self.fkeys_b.all())
    
        # Now we can make the new record
        self.pk = None
        # Make any changes you like to the new instance here, then
        self.save()
    
        foreign_keys = {}
        for fk in fks_to_copy:
            fk.pk = None
            # Likewise make any changes to the related model here
            # However, we avoid calling fk.save() here to prevent
            # hitting the database once per iteration of this loop
            try:
                # Use fk.__class__ here to avoid hard-coding the class name
                foreign_keys[fk.__class__].append(fk)
            except KeyError:
                foreign_keys[fk.__class__] = [fk]
    
        # Now we can issue just two calls to bulk_create,
        # one for fkeys_a and one for fkeys_b
        for cls, list_of_fks in foreign_keys.items():
            cls.objects.bulk_create(list_of_fks)
    

    What it looks like when you use it:

    In [6]: model.id
    Out[6]: 4443
    
    In [7]: model.duplicate()
    
    In [8]: model.id
    Out[8]: 17982
    
    In [9]: old_model = Model.objects.get(id=4443)
    
    In [10]: old_model.fkeys_a.count()
    Out[10]: 2
    
    In [11]: old_model.fkeys_b.count()
    Out[11]: 1
    
    In [12]: model.fkeys_a.count()
    Out[12]: 2
    
    In [13]: model.fkeys_b.count()
    Out[13]: 1
    

    Model and related_model names changed to protect the innocent.

提交回复
热议问题