Copying a model instance, i.e. creating a new database row based on an existing one (copying all the data except the primary key), doesn’t seem to be a very common need – it’s not directly supported by the Django ORM, and Google doesn’t turn much up, either.
But maybe that’s because, in most cases, it really is pretty simple:
>>> obj = Model.objects.get(pk=1) >>> obj.pk = None # (or: obj.id=None) >>> obj.save() >>> obj.pk 2
That’s how I’ve done it myself in the past. However, it turns out that this doesn’t work when model inheritance is used. When copying a subclass model, obj.id refers to the parent model object’s primary key, and obj.pk points to the parent_link OneToOne relationship. Both need to be nulled for a copy to be created, otherwise the save() logic will cause the missing one to be reset to the original value (I haven’t gone through all of the code, but that seems to be the effect).
>>> obj = SubclassModel.objects.get(pk=1) >>> obj.pk = None >>> obj.id = None >>> obj.save() >>> obj.pk 2
While that works, I felt like it would be relying on implementation details a bit too much, so currently I am using this function:
from django.db.models import AutoField def copy_model_instance(obj): """Create a copy of a model instance. M2M relationships are currently not handled, i.e. they are not copied. See also Django #4027. """ initial = dict([(f.name, getattr(obj, f.name)) for f in obj._meta.fields if not isinstance(f, AutoField) and not f in obj._meta.parents.values()]) return obj.__class__(**initial)
Which basically initializes a new model instance with the data from the old, while leaving out all id and parent link fields. For example:
>>> obj = SubclassModel.objects.get(pk=1) >>> obj = copy_model_instance(obj) >>> obj.save() >>> obj.pk >>> 2
So far it works great, and I’m pretty happy with it – I’d love to hear other ideas, though.
This is updating the same instance instead of cloning that instance.
LikeLike
The Python instance remains the same, but a new database row is created. That is how it worked for me at the time, at least.
LikeLike
Thank you, I have updated it as per my need.
LikeLike