Ho incontrato un paio di gotcha con la risposta accettata. Ecco la mia soluzione
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: questo utilizza soluzioni che non sono ufficialmente sanzionate nei documenti Django e potrebbero smettere di funzionare nelle versioni future. Ho provato questo in 1.9.13.
Il primo miglioramento è che ti consente di continuare a usare l'istanza originale, usando copy.copy
. Anche se non intendi riutilizzare l'istanza, può essere più sicuro eseguire questo passaggio se l'istanza che stai clonando è stata passata come argomento a una funzione. In caso contrario, il chiamante avrà inaspettatamente un'istanza diversa quando viene restituita la funzione.
copy.copy
sembra produrre una copia superficiale di un'istanza del modello Django nel modo desiderato. Questa è una delle cose che non ho trovato documentato, ma funziona decapando e scartando, quindi probabilmente è ben supportato.
In secondo luogo, la risposta approvata lascerà tutti i risultati precaricati allegati alla nuova istanza. Tali risultati non dovrebbero essere associati alla nuova istanza, a meno che non si copino esplicitamente le relazioni a molti. Se attraversi le relazioni precaricate, otterrai risultati che non corrispondono al database. Rompere il codice di lavoro quando aggiungi un prefetch può essere una brutta sorpresa.
L'eliminazione _prefetched_objects_cache
è un modo rapido e sporco per eliminare tutti i prefetch. I successivi accessi a molti funzionano come se non ci fosse mai un prefetch. L'uso di una proprietà non documentata che inizia con un carattere di sottolineatura probabilmente richiede problemi di compatibilità, ma per ora funziona.