Encontrei algumas dicas com a resposta aceita. Aqui está a minha solução.
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
Nota: isso usa soluções que não são oficialmente sancionadas nos documentos do Django e podem deixar de funcionar em versões futuras. Eu testei isso em 1.9.13.
A primeira melhoria é que ela permite que você continue usando a instância original, usando copy.copy
. Mesmo que você não pretenda reutilizar a instância, pode ser mais seguro executar esta etapa se a instância que você está clonando foi passada como argumento para uma função. Caso contrário, o chamador inesperadamente terá uma instância diferente quando a função retornar.
copy.copy
parece produzir uma cópia superficial de uma instância do modelo Django da maneira desejada. Essa é uma das coisas que não encontrei documentada, mas funciona com decapagem e remoção, portanto provavelmente é bem suportada.
Em segundo lugar, a resposta aprovada deixará quaisquer resultados pré-buscados anexados à nova instância. Esses resultados não devem ser associados à nova instância, a menos que você copie explicitamente os relacionamentos entre muitos. Se você percorrer os relacionamentos pré-buscados, obterá resultados que não correspondem ao banco de dados. Quebrar o código de funcionamento quando você adiciona uma pré-busca pode ser uma surpresa desagradável.
A exclusão _prefetched_objects_cache
é uma maneira rápida e suja de remover todos os prefets. Os acessos subsequentes a muitos funcionam como se nunca houvesse uma pré-busca. O uso de uma propriedade não documentada que começa com um sublinhado provavelmente está solicitando problemas de compatibilidade, mas funciona por enquanto.