Я столкнулся с парой ошибок с принятым ответом. Вот мое решение.
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 желаемым способом. Это одна из вещей, которые я не нашел документированных, но она работает путем травления и расслоения, так что, вероятно, это хорошо поддерживается.
Во-вторых, утвержденный ответ оставит все предварительно выбранные результаты прикрепленными к новому экземпляру. Эти результаты не должны быть связаны с новым экземпляром, если вы явно не скопировали отношения ко многим. Если вы пересекаете предварительно выбранные отношения, вы получите результаты, которые не соответствуют базе данных. Нарушение рабочего кода при добавлении предварительной выборки может быть неприятным сюрпризом.
Удаление _prefetched_objects_cache
- это быстрый и грязный способ удалить все предварительные выборки. Последующие обращения к множеству обращений работают так, как будто предварительной выборки никогда не было. Использование недокументированного свойства, начинающегося со знака подчеркивания, вероятно, вызывает проблему совместимости, но пока работает.