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



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


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



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

700   10  

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

    Ничего не найдено.