Как клонировать объект экземпляра модели Django и сохранить его в базе данных?
Foo.objects.get(pk="foo")
<Foo: test>
в базе данных я хочу добавить другой объект, который является копией объекта выше.
полагаю, что моя таблица имеет одну строку. Я хочу вставить объект первой строки в другую строку с другим первичным ключом. Как я могу это сделать?
10 ответов
просто измените первичный ключ вашего объекта и запустите save().
obj = Foo.objects.get(pk=<some_existing_pk>)
obj.pk = None
obj.save()
Если вы хотите автоматически сгенерированный ключ, установите новый ключ в None.
подробнее об обновлении / вставке здесь.
документация 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: установка ПК в 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()