Как клонировать объект экземпляра модели Django и сохранить его в базе данных?
Foo.objects.get(pk="foo")
<Foo: test>
в базе данных, я хочу добавить еще один объект, который является копией вышеуказанного объекта.
полагаю, что моя таблица имеет одну строку. Я хочу вставить первый объект строки в другую строку с другим первичным ключом. Как я могу это сделать?
10 ответов:
просто измените первичный ключ вашего объекта и запустите save().
obj = Foo.objects.get(pk=<some_existing_pk>) obj.pk = None obj.save()Если вы хотите автоматически сгенерированный ключ, установите новый ключ В Нет.
подробнее об обновлении / вставке здесь.
документация 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функция для классов моделей, но, насколько я знаю, они решили пока не решать эту проблему. Так что этот "ручной" способ копирования, вероятно, придется сделать на данный момент.
будьте осторожны здесь. Это может быть очень дорого, если вы находитесь в цикле какого-то рода, и вы извлекаете объекты один за другим. Если вы не хотите вызывать базу данных, просто сделайте:
from copy import deepcopy new_instance = deepcopy(object_you_want_copied) new_instance.id = None new_instance.save()Он делает то же самое, что и некоторые из этих других ответов, но он не вызывает вызов базы данных для получения объекта. Это также полезно, если вы хотите создать копию объекта, который не существует в базе данных.
Как это сделать был добавлен в официальные документы Django в Django1. 4
https://docs.djangoproject.com/en/1.10/topics/db/queries/#copying-model-instances
официальный ответ похож на ответ miah, но документы указывают на некоторые трудности с наследованием и связанными объектами, поэтому вы, вероятно, должны убедиться, что Вы читаете документы.
есть фрагмент клона здесь, который вы можете добавить в свою модель, которая делает это:
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)
используйте следующий код:
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)
установка pk в None лучше, sinse Django может правильно создать pk для вас
object_copy = MyObject.objects.get(pk=...) object_copy.pk = None object_copy.save()
я столкнулся с парой gotchas с принятым ответом. Вот мое решение.
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Примечание: это использует решения, которые официально не санкционированы в документах Django, и они могут перестать работать в будущих версиях. Я проверил это в 1.9.13.
первое улучшение заключается в том, что он позволяет продолжать использовать исходный экземпляр, используя
copy.copy. Даже если вы не собираетесь повторно использовать экземпляр, это может быть безопаснее сделать, если экземпляр вы клонируете был передан в качестве аргумента функции. Если нет, то вызывающий объект неожиданно будет иметь другой экземпляр при возврате функции.
copy.copyКажется, чтобы произвести неглубокую копию экземпляра модели Django в нужном направлении. Это одна из вещей, которые я не нашел документально, но она работает путем маринования и распалубки, поэтому она, вероятно, хорошо поддерживается.во-вторых, утвержденный ответ оставит все предварительно выбранные результаты, прикрепленные к новому экземпляру. Эти результаты не должны быть связанным с новым экземпляром, если вы явно не копируете отношения to-many. Если вы пройдете через предварительно выбранные отношения, вы получите результаты, которые не соответствуют базе данных. Нарушение рабочего кода при добавлении предварительной выборки может быть неприятным сюрпризом.
удаление
_prefetched_objects_cacheэто быстрый и грязный способ убрать все префетчи. После-многие обращения работают так, как будто никогда не было предварительной выборки. Использование недокументированного свойства, которое начинается с подчеркивания, вероятно прошу проблемы совместимости, но пока это работает.
клонировать модель с несколькими уровнями наследования, т. е. >= 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)пожалуйста, обратитесь к вопросу здесь.
попробуй такое
original_object = Foo.objects.get(pk="foo") v = vars(original_object) v.pop("pk") new_object = Foo(**v) new_object.save()
Comments