web-dev-qa-db-de.com

Wie kann ich ein Django-Modellinstanzobjekt klonen und in der Datenbank speichern?

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

In der Datenbank möchte ich ein anderes Objekt hinzufügen, bei dem es sich um eine Kopie des obigen Objekts handelt.

Angenommen, meine Tabelle hat eine Zeile. Ich möchte das erste Zeilenobjekt in eine andere Zeile mit einem anderen Primärschlüssel einfügen. Wie kann ich das machen?

210
user426795

Ändern Sie einfach den Primärschlüssel Ihres Objekts und führen Sie save () aus.

obj = Foo.objects.get(pk=<some_existing_pk>)
obj.pk = None
obj.save()

Wenn Sie einen automatisch generierten Schlüssel wünschen, setzen Sie den neuen Schlüssel auf Keine.

Mehr zu UPDATE/INSERT hier .

355
miah

Die Django-Dokumentation für Datenbankabfragen enthält einen Abschnitt zum Kopieren von Modellinstanzen . Angenommen, Ihre Primärschlüssel werden automatisch generiert, Sie erhalten das Objekt, das Sie kopieren möchten, setzen den Primärschlüssel auf None und speichern das Objekt erneut:

blog = Blog(name='My blog', tagline='Blogging is easy')
blog.save() # blog.pk == 1

blog.pk = None
blog.save() # blog.pk == 2

In diesem Snippet erstellt das erste save() das ursprüngliche Objekt und das zweite save() erstellt die Kopie.

Wenn Sie weiter in der Dokumentation lesen, gibt es auch Beispiele, wie mit zwei komplexeren Fällen umgegangen werden soll: (1) Kopieren eines Objekts, das eine Instanz einer Modellunterklasse ist, und (2) Kopieren verwandter Objekte, einschließlich der Objekte in Many-to -Viele Beziehungen.


Anmerkung zu miahs Antwort: Die Einstellung von pk auf None wird in miahs Antwort erwähnt, obwohl sie nicht vorne und mittig dargestellt wird. Meine Antwort dient also hauptsächlich dazu, diese Methode als von Django empfohlene Methode hervorzuheben.

Historischer Hinweis: Dies wurde in den Django-Dokumenten erst in Version 1.4 erläutert. Dies ist jedoch seit 1.4 möglich.

Mögliche zukünftige Funktionalität: Die vorgenannte Dokumentänderung wurde in dieses Ticket vorgenommen. Im Kommentarthread des Tickets wurde auch über das Hinzufügen einer eingebauten copy-Funktion für Modellklassen diskutiert, aber soweit ich weiß, haben sie beschlossen, dieses Problem noch nicht zu lösen. Diese "manuelle" Art des Kopierens wird also wahrscheinlich erst einmal erledigt sein müssen.

118
S. Kirby

Sei hier vorsichtig. Dies kann extrem teuer sein, wenn Sie sich in einer Schleife befinden und Objekte einzeln abrufen. Wenn Sie den Aufruf der Datenbank nicht wünschen, machen Sie einfach Folgendes:

from copy import deepcopy

new_instance = deepcopy(object_you_want_copied)
new_instance.id = None
new_instance.save()

Es macht dasselbe wie einige dieser anderen Antworten, es wird jedoch nicht die Datenbank aufgerufen, um ein Objekt abzurufen. Dies ist auch nützlich, wenn Sie eine Kopie eines Objekts erstellen möchten, das noch nicht in der Datenbank vorhanden ist.

37
Troy Grosfield

Es gibt ein Klon-Snippet here , das Sie zu Ihrem Modell hinzufügen können, das dies tut:

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)
19
Dominic Rodger

Wie Sie dies tun, wurde den offiziellen Django-Dokumenten in Django1.4 hinzugefügt

https://docs.djangoproject.com/de/1.10/topics/db/queries/#copying-model-instances

Die offizielle Antwort ist ähnlich wie die von miah, aber die Dokumente weisen auf einige Schwierigkeiten bei der Vererbung und verwandten Objekten hin. Daher sollten Sie wahrscheinlich sicherstellen, dass Sie die Dokumente lesen.

19
Michael Bylstra

Verwenden Sie den folgenden Code:

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)
18
t_io

wenn pk auf None eingestellt ist, ist besser, sinse Django kann einen pk für Sie richtig erstellen

object_copy = MyObject.objects.get(pk=...)
object_copy.pk = None
object_copy.save()
4
Ardine

Ich bin auf ein paar Gotchas mit der akzeptierten Antwort gestoßen. Hier ist meine Lösung.

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

Hinweis: Hierbei werden Lösungen verwendet, die in den Django-Dokumenten nicht offiziell genehmigt sind, und funktionieren möglicherweise in zukünftigen Versionen nicht mehr. Ich habe das in 1.9.13 getestet.

Die erste Verbesserung besteht darin, dass Sie die ursprüngliche Instanz weiterhin verwenden können, indem Sie copy.copy verwenden. Selbst wenn Sie die Instanz nicht wiederverwenden möchten, kann es sicherer sein, diesen Schritt auszuführen, wenn die zu kopierende Instanz als Argument an eine Funktion übergeben wurde. Wenn nicht, hat der Aufrufer unerwartet eine andere Instanz, wenn die Funktion zurückkehrt.

copy.copy scheint in der gewünschten Weise eine flache Kopie einer Django-Modellinstanz zu erzeugen. Dies ist eines der Dinge, die ich nicht dokumentiert fand, aber es funktioniert durch Beizen und Abnehmen, daher ist es wahrscheinlich gut unterstützt.

Zweitens hinterlässt die genehmigte Antwort alle vorabgerufenen Ergebnisse der neuen Instanz. Diese Ergebnisse sollten nicht mit der neuen Instanz verknüpft werden, es sei denn, Sie kopieren die Zu-Viele-Beziehungen explizit. Wenn Sie die vorabgerufenen Beziehungen durchlaufen, erhalten Sie Ergebnisse, die nicht mit der Datenbank übereinstimmen. Das Brechen von Arbeitscode beim Hinzufügen eines Prefetch kann eine böse Überraschung sein.

Durch das Löschen von _prefetched_objects_cache können Sie schnell alle Vorabrufe entfernen. In der Folge funktionieren viele Zugriffe so, als ob es nie einen Prefetch gab. Die Verwendung einer undokumentierten Eigenschaft, die mit einem Unterstrich beginnt, erfordert möglicherweise Kompatibilitätsprobleme, ist jedoch vorerst funktionsfähig.

3
morningstar

Versuche dies

original_object = Foo.objects.get(pk="foo")
v = vars(original_object)
v.pop("pk")
new_object = Foo(**v)
new_object.save()
0
Pulkit Pahwa

Dies ist eine weitere Möglichkeit, die Modellinstanz zu klonen:

d = Foo.objects.filter(pk=1).values().first()   
d.update({'id': None})
duplicate = Foo.objects.create(**d)
0
Ahtisham

So klonen Sie ein Modell mit mehreren Vererbungsstufen, d. H.> = 2 oder ModelC (unten)

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)

Bitte beziehen Sie sich auf die Frage hier .

0
David Cheung