Me he encontrado con un par de trampas con la respuesta aceptada. Aquí está mi solución.
import copy
def clone(instance):
cloned = copy.copy(instance) # don't alter original instance
cloned.pk = None
try:
delattr(cloned, '_prefetched_objects_cache')
except AttributeError:
pass
return cloned
Nota: esto utiliza soluciones que no están oficialmente autorizadas en los documentos de Django, y pueden dejar de funcionar en futuras versiones. Probé esto en 1.9.13.
La primera mejora es que le permite continuar utilizando la instancia original, utilizando copy.copy
. Incluso si no tiene la intención de reutilizar la instancia, puede ser más seguro realizar este paso si la instancia que está clonando se pasó como argumento a una función. De lo contrario, la persona que llama inesperadamente tendrá una instancia diferente cuando la función regrese.
copy.copy
parece producir una copia superficial de una instancia de modelo Django de la manera deseada. Esta es una de las cosas que no encontré documentada, pero funciona encurtiendo y desenredando, por lo que probablemente tenga un buen soporte.
En segundo lugar, la respuesta aprobada dejará los resultados obtenidos previamente adjuntos a la nueva instancia. Esos resultados no deben asociarse con la nueva instancia, a menos que copie explícitamente las relaciones de muchos. Si recorre las relaciones captadas previamente, obtendrá resultados que no coinciden con la base de datos. Romper el código de trabajo cuando agrega una captación previa puede ser una desagradable sorpresa.
Eliminar _prefetched_objects_cache
es una forma rápida y sucia de eliminar todas las captaciones previas. Los accesos posteriores a muchos funcionan como si nunca hubiera una captación previa. El uso de una propiedad no documentada que comienza con un guión bajo probablemente requiera problemas de compatibilidad, pero funciona por ahora.