Ich habe Modell Foo, das Feldleiste hat. Das Balkenfeld sollte eindeutig sein, aber Nullen zulassen, was bedeutet, dass ich mehr als einen Datensatz zulassen möchte, wenn das Balkenfeld vorhanden ist null
, aber wenn dies nicht null
der Fall ist , müssen die Werte eindeutig sein.
Hier ist mein Modell:
class Foo(models.Model):
name = models.CharField(max_length=40)
bar = models.CharField(max_length=40, unique=True, blank=True, null=True, default=None)
Und hier ist die entsprechende SQL für die Tabelle:
CREATE TABLE appl_foo
(
id serial NOT NULL,
"name" character varying(40) NOT NULL,
bar character varying(40),
CONSTRAINT appl_foo_pkey PRIMARY KEY (id),
CONSTRAINT appl_foo_bar_key UNIQUE (bar)
)
Wenn ich die Admin-Oberfläche verwende, um mehr als 1 foo-Objekte zu erstellen, bei denen der Balken null ist, wird der Fehler angezeigt: "Foo mit diesem Balken ist bereits vorhanden."
Wenn ich jedoch in die Datenbank (PostgreSQL) einfüge:
insert into appl_foo ("name", bar) values ('test1', null)
insert into appl_foo ("name", bar) values ('test2', null)
Dies funktioniert, ganz gut, es ermöglicht mir, mehr als einen Datensatz einzufügen, wobei der Balken null ist. Die Datenbank ermöglicht mir also, das zu tun, was ich will. Es stimmt einfach nicht mit dem Django-Modell. Irgendwelche Ideen?
BEARBEITEN
Die Portabilität der Lösung für die DB ist kein Problem, wir sind mit Postgres zufrieden. Ich habe mit einem abrufbaren einzigartigen versucht Einstellung, die meine Funktion Wahr / Falsch für bestimmte Werte von zurückkehrt bar , es hat keine Fehler geben, aber gefalzt wie es keinen Effekt auf alle hatte.
Bisher habe ich den eindeutigen Bezeichner aus der Balkeneigenschaft entfernt und die Eindeutigkeit der Balken in der Anwendung behandelt, suche jedoch immer noch nach einer eleganteren Lösung. Irgendwelche Empfehlungen?
quelle
def get_db_prep_value(self, value, connection, prepared=False)
als Methodenaufruf benötigen . Weitere Informationen finden Sie unter groups.google.com/d/msg/django-users/Z_AXgg2GCqs/zKEsfu33OZMJ . Die folgende Methode funktioniert auch für mich: def get_prep_value (self, value): Wenn value == "": #wenn Django versucht, einen String zu speichern, senden Sie die Datenbank None (NULL) return None else: Rückgabewert #otherwise, just Übergeben Sie den WertAntworten:
Django hat NULL zum Zweck der Eindeutigkeitsprüfung nicht als gleich NULL angesehen, seit Ticket Nr. 9039 repariert wurde, siehe:
http://code.djangoproject.com/ticket/9039
Das Problem hierbei ist, dass der normalisierte "leere" Wert für ein Formular CharField eine leere Zeichenfolge ist, nicht None. Wenn Sie das Feld leer lassen, wird in der Datenbank eine leere Zeichenfolge und nicht NULL gespeichert. Leere Zeichenfolgen entsprechen leeren Zeichenfolgen für Eindeutigkeitsprüfungen, sowohl nach Django- als auch nach Datenbankregeln.
Sie können die Administrationsoberfläche zwingen, NULL für eine leere Zeichenfolge zu speichern, indem Sie Ihr eigenes benutzerdefiniertes Modellformular für Foo mit einer clean_bar-Methode bereitstellen, die die leere Zeichenfolge in None umwandelt:
quelle
fields
oderexclude
AttributModelForm
. Sie können diesMeta
umgehen, indem Sie die innere Klasse in der ModelForm für die Verwendung in admin weglassen. Referenz: docs.djangoproject.com/de/1.10/ref/contrib/admin/…** edit 30.11.2015 : In Python 3 wird die modul -globale
__metaclass__
Variable nicht mehr unterstützt . Zusätzlich wurde abDjango 1.10
derSubfieldBase
Klasse veraltet :Daher muss diese Lösung , wie in der
from_db_value()
Dokumentation und in diesem Beispiel vorgeschlagen , geändert werden in:Ich denke, ein besserer Weg als das Überschreiben der bereinigten_Daten im Administrator wäre, das Zeichenfeld zu unterklassifizieren - auf diese Weise funktioniert es "einfach", egal welches Formular auf das Feld zugreift. Sie können
''
den NULL abfangen, bevor er an die Datenbank gesendet wird, und den NULL abfangen, sobald er aus der Datenbank kommt, und der Rest von Django wird es nicht wissen / sich darum kümmern. Ein schnelles und schmutziges Beispiel:Für mein Projekt habe ich dies in eine
extras.py
Datei geschrieben, die sich im Stammverzeichnis meiner Site befindet. Dann kann ich es einfachfrom mysite.extras import CharNullField
in dermodels.py
Datei meiner App tun. Das Feld verhält sich wie ein CharField. Denken Sie daran, esblank=True, null=True
beim Deklarieren des Felds festzulegen. Andernfalls wird Django einen Validierungsfehler auslösen (Feld erforderlich) oder eine Datenbankspalte erstellen, die NULL nicht akzeptiert.quelle
CharField
zu seinCharNullField
, müssen Sie dies in drei Schritten tun. Fügen Sienull=True
zunächst das Feld hinzu und migrieren Sie es. Führen Sie dann eine Datenmigration durch, um alle leeren Werte so zu aktualisieren, dass sie Nullen sind. Konvertieren Sie schließlich das Feld in CharNullField. Wenn Sie das Feld vor der Datenmigration konvertieren, führt Ihre Datenmigration nichts aus.from_db_value()
dieser zusätzlichecontex
Parameter nicht vorhanden sein sollte . Es sollte seindef from_db_value(self, value, expression, connection):
Da ich neu im Stackoverflow bin, darf ich noch nicht auf Antworten antworten, aber ich möchte darauf hinweisen, dass ich aus philosophischer Sicht der beliebtesten Antwort auf diese Frage nicht zustimmen kann. (von Karen Tracey)
Das OP verlangt, dass sein Balkenfeld eindeutig ist, wenn es einen Wert hat, andernfalls null. Dann muss das Modell selbst sicherstellen, dass dies der Fall ist. Es kann nicht dem externen Code überlassen werden, dies zu überprüfen, da dies bedeuten würde, dass es umgangen werden kann. (Oder Sie können vergessen, dies zu überprüfen, wenn Sie in Zukunft eine neue Ansicht schreiben.)
Um Ihren Code wirklich OOP zu halten, müssen Sie daher eine interne Methode Ihres Foo-Modells verwenden. Das Ändern der save () -Methode oder des Felds ist eine gute Option, die Verwendung eines Formulars hierfür jedoch mit Sicherheit nicht.
Persönlich bevorzuge ich die Verwendung des vorgeschlagenen CharNullField, um die Portabilität auf Modelle zu gewährleisten, die ich möglicherweise in Zukunft definieren werde.
quelle
Die schnelle Lösung lautet:
quelle
MyModel.objects.bulk_create()
diese Methode umgehen würde.Eine andere mögliche Lösung
quelle
Dies wurde behoben, nachdem https://code.djangoproject.com/ticket/4136 behoben wurde. In Django 1.11+ können Sie verwenden,
models.CharField(unique=True, null=True, blank=True)
ohne leere Werte manuell in konvertieren zu müssenNone
.quelle
Ich hatte vor kurzem die gleiche Anforderung. Anstatt verschiedene Felder in Unterklassen zu unterteilen, habe ich die Methode save () in meinem Modell (unten 'MyModel' genannt) wie folgt überschrieben:
quelle
Wenn Sie ein Modell MyModel haben und möchten, dass my_field Null oder eindeutig ist, können Sie die Speichermethode des Modells überschreiben:
Auf diese Weise darf das Feld nicht leer sein, sondern ist nur nicht leer oder null. Nullen widersprechen nicht der Eindeutigkeit
quelle
Zum Guten oder Schlechten hält Django
NULL
diesNULL
für Zwecke der Eindeutigkeitsprüfung für gleichwertig . Es führt kein Weg daran vorbei, eine eigene Implementierung der Eindeutigkeitsprüfung zu schreiben, die alsNULL
einzigartig angesehen wird, unabhängig davon, wie oft sie in einer Tabelle vorkommt.(und denken Sie daran, dass einige DB-Lösungen dieselbe Ansicht vertreten
NULL
, sodass Code, der sich auf die Ideen einer DB stützt,NULL
möglicherweise nicht auf andere portierbar ist.)quelle
Sie können dieses Feld
UniqueConstraint
mit der Bedingung hinzufügennullable_field=null
und nicht in diefields
Liste aufnehmen. Wenn Sie auch eine Einschränkung benötigennullable_field
, deren Wert nicht istnull
, können Sie eine zusätzliche hinzufügen.Hinweis: UniqueConstraint wurde seit Django 2.2 hinzugefügt
quelle