I've run into a couple gotchas with the accepted answer. Here is my solution.
import copy
def clone(instance):
cloned = copy.copy(instance)
cloned.pk = None
try:
delattr(cloned, '_prefetched_objects_cache')
except AttributeError:
pass
return cloned
Note: this uses solutions that aren't officially sanctioned in the Django docs, and they may cease to work in future versions. I tested this in 1.9.13.
The first improvement is that it allows you to continue using the original instance, by using copy.copy
. Even if you don't intend to reuse the instance, it can be safer to do this step if the instance you're cloning was passed as an argument to a function. If not, the caller will unexpectedly have a different instance when the function returns.
copy.copy
seems to produce a shallow copy of a Django model instance in the desired way. This is one of the things I did not find documented, but it works by pickling and unpickling, so it's probably well-supported.
Secondly, the approved answer will leave any prefetched results attached to the new instance. Those results shouldn't be associated with the new instance, unless you explicitly copy the to-many relationships. If you traverse the the prefetched relationships, you will get results that don't match the database. Breaking working code when you add a prefetch can be a nasty surprise.
Deleting _prefetched_objects_cache
is a quick-and-dirty way to strip away all prefetches. Subsequent to-many accesses work as if there never was a prefetch. Using an undocumented property that begins with an underscore is probably asking for compatibility trouble, but it works for now.