Angenommen, meine models.py ist wie folgt:
class Character(models.Model):
name = models.CharField(max_length=255)
is_the_chosen_one = models.BooleanField()
Ich möchte nur eine meiner Character
Instanzen is_the_chosen_one == True
und alle anderen haben is_the_chosen_one == False
. Wie kann ich am besten sicherstellen, dass diese Eindeutigkeitsbeschränkung eingehalten wird?
Bestnoten für Antworten, die die Wichtigkeit der Einhaltung der Einschränkungen auf Datenbank-, Modell- und (Administrator-) Formularebene berücksichtigen!
database
django
django-models
django-admin
django-forms
sampablokuper
quelle
quelle
through
Tabelle ,ManyToManyField
dass eine brauchtunique_together
Einschränkung.Antworten:
Wann immer ich diese Aufgabe ausführen musste, habe ich die Speichermethode für das Modell überschrieben und prüfen lassen, ob für ein anderes Modell das Flag bereits gesetzt ist (und es deaktivieren).
quelle
save(self)
,save(self, *args, **kwargs)
aber die Bearbeitung wurde abgelehnt. Könnte sich einer der Rezensenten Zeit nehmen, um zu erklären, warum - da dies mit der Best Practice von Django in Einklang zu stehen scheint.get()
das Character-Objekt zu bearbeiten und es dann erneut zu bearbeiten, müssensave()
Sie nur filtern und aktualisieren, wodurch nur eine SQL-Abfrage erstellt wird und hilft, die DB konsistent zu halten:if self.is_the_chosen_one:
<newline>Character.objects.filter(is_the_chosen_one=True).update(is_the_chosen_one=False)
<newline>super(Character, self).save(*args, **kwargs)
transaction.atomic
was hier wichtig ist. Es ist auch effizienter, eine einzelne Abfrage zu verwenden.Ich würde die Speichermethode des Modells überschreiben. Wenn Sie den Booleschen Wert auf True gesetzt haben, stellen Sie sicher, dass alle anderen auf False gesetzt sind.
Ich habe versucht, die ähnliche Antwort von Adam zu bearbeiten, aber sie wurde abgelehnt, weil zu viel von der ursprünglichen Antwort geändert wurde. Diese Methode ist prägnanter und effizienter, da die Überprüfung anderer Einträge in einer einzigen Abfrage erfolgt.
quelle
save
eine@transaction.atomic
Transaktion abzuschließen. Weil es passieren kann, dass Sie alle Flags entfernen, das Speichern dann jedoch fehlschlägt und alle Zeichen nicht ausgewählt werden.@transaction.atomic
schützt auch vor Rennbedingungen.with transaction.atomic:
innerhalb der if-Anweisung zusammen mit dem Speichern innerhalb der if-Anweisung zu verwenden. Fügen Sie dann einen else-Block hinzu und speichern Sie ihn im else-Block.Anstatt das Bereinigen / Speichern von benutzerdefinierten Modellen zu verwenden, habe ich ein benutzerdefiniertes Feld erstellt, das die
pre_save
Methode überschreibtdjango.db.models.BooleanField
. Anstatt einen Fehler auszulösen, wenn ein anderes Feld vorhanden warTrue
, habe ich alle anderen Felder erstellt,False
wenn dies der Fall warTrue
. Anstatt einen Fehler auszulösen, wenn das Feld warFalse
und kein anderes Feld warTrue
, habe ich das Feld als gespeichertTrue
fields.py
models.py
quelle
Return True
zusetattr(model_instance, self.attname, True)
true
Sie die einzigetrue
Zeile löschen .Die folgende Lösung ist etwas hässlich, könnte aber funktionieren:
Wenn Sie is_the_chosen_one auf False oder None setzen, ist es immer NULL. Sie können so viel NULL haben, wie Sie möchten, aber Sie können nur ein True haben.
quelle
Beim Versuch, mit den Antworten hier über die Runden zu kommen, stelle ich fest, dass einige von ihnen dasselbe Problem erfolgreich angehen und jedes für unterschiedliche Situationen geeignet ist:
Ich würde wählen:
@semente : Respektiert die Einschränkung auf Datenbank-, Modell- und Administratorformularebene, während Django ORM so wenig wie möglich überschrieben wird. Darüber hinaus kann es
wahrscheinlichin einerthrough
TabelleManyToManyField
in einerunique_together
Situation verwendet werden.(Ich werde es überprüfen und berichten)@Ellis Percival : Trifft die Datenbank nur ein zusätzliches Mal und akzeptiert den aktuellen Eintrag als den ausgewählten. Sauber und elegant.
Andere Lösungen, die für meinen Fall nicht geeignet, aber realisierbar sind:
@nemocorp überschreibt die
clean
Methode zur Durchführung einer Validierung. Es wird jedoch nicht zurückgemeldet, welches Modell "das" ist, und dies ist nicht benutzerfreundlich. Trotzdem ist es ein sehr netter Ansatz, besonders wenn jemand nicht so aggressiv sein will wie @Flyte.@ saul.shanabrook und @Thierry J. würden ein benutzerdefiniertes Feld erstellen, das entweder einen anderen "is_the_one" -Eintrag in a ändert
False
oder a auslöstValidationError
. Ich zögere nur, neue Funktionen für meine Django-Installation zu implementieren, es sei denn, dies ist unbedingt erforderlich.@daigorocub : Verwendet Django-Signale. Ich finde es ein einzigartiger Ansatz und gebe einen Hinweis auf die Verwendung von Django-Signalen . Ich bin mir jedoch nicht sicher, ob dies streng genommen eine "ordnungsgemäße" Verwendung von Signalen ist, da ich dieses Verfahren nicht als Teil einer "entkoppelten Anwendung" betrachten kann.
quelle
save()
Operation fehlschlägt!Sie können das obige Formular auch für Administratoren verwenden. Verwenden Sie einfach
quelle
Dadurch wurde die Validierung im Basisverwaltungsformular verfügbar
quelle
Es ist einfacher, diese Art von Einschränkung nach Django Version 2.2 zu Ihrem Modell hinzuzufügen. Sie können direkt verwenden
UniqueConstraint.condition
. Django DocsÜberschreiben Sie einfach Ihre Modelle
class Meta
wie folgt:quelle
Und das ist alles.
quelle
Mit einem ähnlichen Ansatz wie Saul, aber etwas anderem Zweck:
Diese Implementierung löst ein aus,
ValidationError
wenn versucht wird, einen anderen Datensatz mit dem Wert True zu speichern.Außerdem habe ich das
unique_for
Argument hinzugefügt, das für jedes andere Feld im Modell festgelegt werden kann, um die Eindeutigkeit nur für Datensätze mit demselben Wert zu überprüfen, z.quelle
Bekomme ich Punkte für die Beantwortung meiner Frage?
Das Problem war, dass es sich in der Schleife befand, behoben durch:
quelle
Ich habe einige dieser Lösungen ausprobiert und bin aus Gründen der Codekürzung zu einer anderen gekommen (ich muss keine Formulare überschreiben oder Methoden speichern). Damit dies funktioniert, kann das Feld in seiner Definition nicht eindeutig sein, aber das Signal stellt sicher, dass dies geschieht.
quelle
Update 2020, um Anfängern die Arbeit zu erleichtern:
Wenn Sie möchten, dass der eindeutige Boolesche Wert False ist, tauschen Sie natürlich einfach jede Instanz von True gegen False aus und umgekehrt.
quelle