¿Cómo clono un objeto de instancia de modelo Django y lo guardo en la base de datos?


Foo.objects.get(pk="foo")
<Foo: test>

En la base de datos, quiero agregar otro objeto que sea una copia del objeto anterior.

Supongamos que mi tabla tiene una fila. Quiero insertar el objeto de la primera fila en otra fila con una clave primaria diferente. ¿Cómo puedo hacer eso?

Author: Artem Likhvar, 2011-01-19

10 answers

Simplemente cambia la clave principal de tu objeto y ejecuta save().

obj = Foo.objects.get(pk=<some_existing_pk>)
obj.pk = None
obj.save()

Si desea que la clave se genere automáticamente, establezca la nueva clave en Ninguno.

Más información sobre UPDATE/INSERT aquí.

 334
Author: miah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-22 08:24:37

La documentación de Django para consultas de base de datos incluye una sección sobre copiar instancias de modelo. Suponiendo que sus claves principales se generen automáticamente, obtiene el objeto que desea copiar, establece la clave principal en None y guarda el objeto de nuevo:

blog = Blog(name='My blog', tagline='Blogging is easy')
blog.save() # blog.pk == 1

blog.pk = None
blog.save() # blog.pk == 2

En este fragmento, el primero save() crea el objeto original, y el segundo save() crea la copia.

Si sigue leyendo la documentación, también hay ejemplos de cómo manejar dos casos más complejos: (1) copiar un objeto que es una instancia de una subclase de modelo, y (2) también copia objetos relacionados, incluyendo objetos en relaciones de muchos a muchos.


Nota sobre la respuesta de miah: Establecer la pk en None se menciona en la respuesta de miah, aunque no se presenta al frente y al centro. Así que mi respuesta sirve principalmente para enfatizar ese método como la forma recomendada por Django de hacerlo.

Nota histórica: Esto no fue explicado en los documentos de Django hasta la versión 1.4. Ha sido posible desde antes de la 1.4, aunque.

Posible funcionalidad futura: El cambio de documentos antes mencionado se realizó en este ticket. En el hilo de comentarios del ticket, también hubo cierta discusión sobre la adición de una función incorporada copy para las clases modelo, pero por lo que sé, decidieron no abordar ese problema todavía. Así que esta forma" manual " de copiar probablemente tendrá que hacer por ahora.

 111
Author: S. Kirby,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-26 17:47:00

Ten cuidado aquí. Esto puede ser extremadamente costoso si estás en un bucle de algún tipo y estás recuperando objetos uno por uno. Si no desea la llamada a la base de datos, simplemente haga:

from copy import deepcopy

new_instance = deepcopy(object_you_want_copied)
new_instance.id = None
new_instance.save()

Hace lo mismo que algunas de estas otras respuestas, pero no hace una llamada a la base de datos para recuperar un objeto. Esto también es útil si desea hacer una copia de un objeto que aún no existe en la base de datos.

 34
Author: Troy Grosfield,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-31 10:26:20

Cómo hacer esto se agregó a los documentos oficiales de Django en Django1. 4

Https://docs.djangoproject.com/en/1.10/topics/db/queries/#copying-model-instances

La respuesta oficial es similar a la respuesta de miah, pero los documentos señalan algunas dificultades con la herencia y los objetos relacionados, por lo que probablemente debería asegurarse de leer los documentos.

 19
Author: Michael Bylstra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-25 05:24:29

Hay un fragmento de clon aquí, que puede agregar a su modelo que hace esto:

def clone(self):
  new_kwargs = dict([(fld.name, getattr(old, fld.name)) for fld in old._meta.fields if fld.name != old._meta.pk]);
  return self.__class__.objects.create(**new_kwargs)
 18
Author: Dominic Rodger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-19 12:27:14

Utilice el siguiente código:

from django.forms import model_to_dict

instance = Some.objects.get(slug='something')

kwargs = model_to_dict(instance, exclude=['id'])
new_instance = Some.objects.create(**kwargs)
 17
Author: t_io,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-20 06:57:13

Establecer pk a Ninguno es mejor, sinse Django puede crear correctamente un pk para usted

object_copy = MyObject.objects.get(pk=...)
object_copy.pk = None
object_copy.save()
 4
Author: Ardine,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-12 07:59:51

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 sancionadas oficialmente en los documentos de Django, y pueden dejar de funcionar en futuras versiones. Probé esto en 1.9.13.

La primera mejora es que permite continuar usando la instancia original, usando 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 argumento a una función. Si no, el llamante tendrá inesperadamente una instancia diferente cuando la función regrese.

copy.copy parece producir una copia superficial de una instancia del modelo Django de la manera deseada. Esta es una de las cosas que no encontré documentadas, pero funciona al decapar y desempaquetar, por lo que probablemente esté bien respaldada.

En segundo lugar, la respuesta aprobada dejará cualquier resultado preestablecido adjunto a la nueva instancia. Esos resultados no deben estar asociados con el nuevo instancia, a menos que copie explícitamente las relaciones a-muchos. Si recorres las relaciones preestablecidas, obtendrás resultados que no coinciden con la base de datos. Romper el código de trabajo al agregar un prefetch puede ser una desagradable sorpresa.

Eliminar _prefetched_objects_cache es una forma rápida y sucia de eliminar todas las prefetches. Subsecuente a-muchos accesos funcionan como si nunca hubiera un prefetch. El uso de una propiedad indocumentada que comienza con un guion bajo probablemente está pidiendo problemas de compatibilidad, pero funciona por ahora.

 2
Author: morningstar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-27 02:29:49

Para clonar un modelo con múltiples niveles de herencia, es decir, > = 2, o ModelC por debajo de

class ModelA(models.Model):
    info1 = models.CharField(max_length=64)

class ModelB(ModelA):
    info2 = models.CharField(max_length=64)

class ModelC(ModelB):
    info3 = models.CharField(max_length=64)

Por favor refiérase a la pregunta aquí.

 0
Author: David Cheung,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:02:46

Prueba esto

original_object = Foo.objects.get(pk="foo")
v = vars(original_object)
v.pop("pk")
new_object = Foo(**v)
new_object.save()
 0
Author: Pulkit Pahwa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-22 10:07:13