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