Gibt es eine Möglichkeit, die Primärschlüssel der Elemente abzurufen, die Sie mit der Funktion mass_create in django 1.4+ erstellt haben?
74
Gibt es eine Möglichkeit, die Primärschlüssel der Elemente abzurufen, die Sie mit der Funktion mass_create in django 1.4+ erstellt haben?
Antworten:
2016
Seit Django 1.10 - es wird jetzt unterstützt (nur auf Postgres) ist hier ein Link zum Dokument .
>>> list_of_objects = Entry.objects.bulk_create([ ... Entry(headline="Django 2.0 Released"), ... Entry(headline="Django 2.1 Announced"), ... Entry(headline="Breaking: Django is awesome") ... ]) >>> list_of_objects[0].id 1
Aus dem Änderungsprotokoll:
quelle
bulk_create
Methode zurückgegebene Liste ist dieselbe, die Sie angegeben haben, und die lokalen Objekte (Mitglieder dieser Liste) haben sie nicht festgelegt, wie pyriku in seiner Antwort demonstriert .Laut Dokumentation können Sie dies nicht tun: https://docs.djangoproject.com/de/dev/ref/models/querysets/#bulk-create
Bulk-Create ist genau dafür: Erstellen Sie viele Objekte auf effiziente Weise und speichern Sie viele Abfragen. Dies bedeutet jedoch, dass die Antwort, die Sie erhalten, unvollständig ist. Wenn Sie tun:
>>> categories = Category.objects.bulk_create([ Category(titel="Python", user=user), Category(titel="Django", user=user), Category(titel="HTML5", user=user), ]) >>> [x.pk for x in categories] [None, None, None]
Das bedeutet nicht, dass Ihre Kategorien kein pk haben, nur dass die Abfrage sie nicht abgerufen hat (wenn der Schlüssel ein ist
AutoField
). Wenn Sie die pks aus irgendeinem Grund möchten, müssen Sie die Objekte auf klassische Weise speichern.quelle
bulk_create
, um die erstellten IDs zuverlässig abzurufen?Zwei Ansätze, die ich mir vorstellen kann:
a) Sie könnten tun
category_ids = Category.objects.values_list('id', flat=True) categories = Category.objects.bulk_create([ Category(title="title1", user=user, created_at=now), Category(title="title2", user=user, created_at=now), Category(title="title3", user=user, created_at=now), ]) new_categories_ids = Category.objects.exclude(id__in=category_ids).values_list('id', flat=True)
Dies kann etwas teuer sein, wenn das Abfrageset extrem groß ist.
b) Wenn das Modell ein
created_at
Feld hat,now = datetime.datetime.now() categories = Category.objects.bulk_create([ Category(title="title1", user=user, created_at=now), Category(title="title2", user=user, created_at=now), Category(title="title3", user=user, created_at=now), ]) new_cats = Category.objects.filter(created_at >= now).values_list('id', flat=True)
Dies hat die Einschränkung, dass ein Feld vorhanden ist, in dem gespeichert wird, wann das Objekt erstellt wurde.
quelle
date_created
Feld, also könnte dies funktionieren, obwohl es nur ein minimaler Aufwand ist, eines hinzuzufügen. Meine einzige Sorge ist, dass mehrere Abfragen gleichzeitigbulk_create
diecreated_at
Datenbank treffen könnten. Ich nehme an, ich muss vor und nach der Abfrage einen Sperrmechanismus implementieren .select max(id) is better
max(id)
, ich habe es versucht und bin auf Probleme gestoßen . In der MariaDB-Dokumentation wird ausdrücklich angegeben, dass nichts anderes als die Einzigartigkeit der PK angenommen werden soll.Eigentlich hat mein Kollege die folgende Lösung vorgeschlagen, die jetzt alles so offensichtlich erscheint. Fügen Sie eine neue Spalte mit dem Namen hinzu,
bulk_ref
die Sie mit einem eindeutigen Wert füllen, und fügen Sie sie für jede Zeile ein.bulk_ref
Fragen Sie anschließend einfach vorher die Tabelle mit dem Set ab und voila, Ihre eingefügten Datensätze werden abgerufen. z.B:cars = [Car( model="Ford", color="Blue", price="5000", bulk_ref=5, ),Car( model="Honda", color="Silver", price="6000", bulk_ref=5, )] Car.objects.bulk_create(cars) qs = Car.objects.filter(bulk_ref=5)
quelle
Ich habe viele Strategien ausprobiert, um diese Einschränkung von MariaDB / MySQL zu umgehen. Die einzige zuverlässige Lösung, die ich am Ende gefunden habe, war das Generieren der Primärschlüssel in der Anwendung. Generieren
INT AUTO_INCREMENT
Sie KEINE PK-Felder selbst, dies funktioniert nicht einmal in einer Transaktion mit Isolationsstufeserializable
, da der PK-Zähler in MariaDB nicht durch Transaktionssperren geschützt ist.Die Lösung besteht darin
UUID
, den Modellen eindeutige Felder hinzuzufügen , ihre Werte in der Modellklasse zu generieren und diese dann als Bezeichner zu verwenden. Wenn Sie eine Reihe von Modellen in der Datenbank speichern, erhalten Sie immer noch nicht die tatsächliche PK zurück, aber das ist in Ordnung, da Sie sie in nachfolgenden Abfragen eindeutig mit ihrer UUID identifizieren können.quelle
In der Django-Dokumentation sind derzeit folgende Einschränkungen aufgeführt:
Aber es gibt gute Nachrichten. Es gab ein paar Tickets, über die
bulk_create
aus dem Gedächtnis gesprochen wurde. Das oben aufgeführte Ticket hat höchstwahrscheinlich eine Lösung, die bald implementiert wird, aber es gibt offensichtlich keine Garantie für die Zeit oder ob es jemals gelingen wird.Es gibt also zwei mögliche Lösungen:
Warten Sie ab, ob dieser Patch die Produktion erreicht. Sie können dabei helfen, indem Sie die angegebene Lösung testen und die Django-Community über Ihre Gedanken / Probleme informieren. https://code.djangoproject.com/attachment/ticket/19527/bulk_create_and_create_schema_django_v1.5.1.patch
Überschreiben / schreiben Sie Ihre eigene Bulk-Insert-Lösung.
quelle
Die wahrscheinlich einfachste Problemumgehung besteht darin, Primärschlüssel manuell zuzuweisen. Es hängt vom jeweiligen Fall ab, aber manchmal reicht es aus, mit max (id) +1 aus der Tabelle zu beginnen und jedem Objekt inkrementelle Nummern zuzuweisen. Wenn jedoch mehrere Clients gleichzeitig Datensätze einfügen können, ist möglicherweise eine Sperre erforderlich.
quelle
Dies funktioniert nicht auf Lager Django, aber es gibt einen Patch im Django-Bug-Tracker , mit dem mass_create die Primärschlüssel für erstellte Objekte festlegt.
quelle
Der von @Or Duan vorgeschlagene Ansatz funktioniert für PostgreSQL bei Verwendung
bulk_create
mitignore_conflicts=False
. Wennignore_conflicts=True
festgelegt, erhalten Sie die Werte für dieAutoField
(normalerweise ID) in den zurückgegebenen Objekten nicht.quelle
# datatime.py # my datatime function def getTimeStamp(needFormat=0, formatMS=True): if needFormat != 0: return datetime.datetime.now().strftime(f'%Y-%m-%d %H:%M:%S{r".%f" if formatMS else ""}') else: ft = time.time() return (ft if formatMS else int(ft)) def getTimeStampString(): return str(getTimeStamp()).replace('.', '') # model bulk_marker = models.CharField(max_length=32, blank=True, null=True, verbose_name='bulk_marker', help_text='ONLYFOR_bulkCreate') # views import .........getTimeStampString data_list( Category(title="title1", bulk_marker=getTimeStampString()), ... ) # bulk_create Category.objects.bulk_create(data_list) # Get primary Key id Category.objects.filter(bulk_marker=bulk_marker).values_list('id', flat=True)
quelle
Das sollte funktionieren.
categories = Category.objects.bulk_create([ Category(titel="Python", user=user), Category(titel="Django", user=user), Category(titel="HTML5", user=user), ]) >>> categories[0] [<Category: Python>] >>> categories[1] [<Category: Django>]
quelle
bulk_create()
setzt nicht die Primärschlüssel für die Objekte, die es erstellt!