Как клонировать объект экземпляра модели Django и сохранять его в базе данных?

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

В базе данных я хочу добавить еще один объект, который является копией объекта выше.

Предположим, что моя таблица имеет одну строку. Я хочу вставить первый объект строки в другую строку с другим первичным ключом. Как я могу это сделать?

Ответ 1

Просто измените первичный ключ вашего объекта и запустите save().

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

Если вы хотите автогенерированный ключ, установите для нового ключа значение None.

Подробнее о UPDATE/INSERT здесь.

Ответ 2

Документация Django для запросов к базе данных включает раздел о копировании экземпляров модели. Предполагая, что ваши первичные ключи автогенерируются, вы получаете объект, который хотите скопировать, установите первичный ключ на None и снова сохраните объект:

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

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

В этом фрагменте первый save() создает исходный объект, а второй save() создает копию.

Если вы продолжаете читать документацию, есть примеры того, как обрабатывать два более сложных случая: (1) копирование объекта, являющегося экземпляром подкласса модели, и (2) также копирование связанных объектов, включая объекты в отношения "многие ко многим".


Примечание для ответа miah: установка pk на None упоминается в ответе miah, хотя он не представлен спереди и в центре. Поэтому мой ответ в основном помогает подчеркнуть этот метод как рекомендуемый Django способ сделать это.

Историческое примечание. Это не объяснялось в документах Django до версии 1.4. Это было возможно, так как до 1.4, однако.

Возможная будущая функциональность: вышеупомянутое изменение документов было сделано в этот билет. В потоке комментариев к билетам также обсуждалось добавление встроенной функции copy для классов моделей, но, насколько я знаю, они решили не решать эту проблему. Таким образом, этот "ручной" способ копирования, вероятно, придется сделать сейчас.

Ответ 3

Будьте осторожны. Это может быть очень дорого, если вы в какой-то петле, и вы возвращаете объекты по одному. Если вам не нужен вызов в базу данных, просто выполните:

from copy import deepcopy

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

Он делает то же самое, что и некоторые из этих других ответов, но не вызывает вызов базы данных для извлечения объекта. Это также полезно, если вы хотите сделать копию объекта, который еще не существует в базе данных.

Ответ 4

Здесь есть фрагмент клонирования здесь, который вы можете добавить к своей модели, которая делает это:

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)

Ответ 5

Как это сделать, было добавлено в официальные документы Django в Django1.4

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

Официальный ответ аналогичен ответу miah, но документы указывают на некоторые трудности с наследованием и связанными с ним объектами, поэтому вы, вероятно, должны убедиться, что прочитали документы.

Ответ 6

Используйте приведенный ниже код:

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)

Ответ 7

установка pk на None лучше, sinse Django может правильно создать pk для вас

object_copy = MyObject.objects.get(pk=...)
object_copy.pk = None
object_copy.save()

Ответ 8

Чтобы клонировать модель с несколькими уровнями наследования, т.е. >= 2, или ModelC ниже

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)

Пожалуйста, обратитесь к вопросу здесь.

Ответ 9

Попробуйте это

original_object = Foo.objects.get(pk="foo")
v = vars(original_object)
v.pop("pk")
new_object = Foo(**v)
new_object.save()