Wie in den Kommentaren erwähnt, besteht der Grund dafür, dass "bei dieser Einrichtung beides benötigt wird" darin, dass Sie vergessen haben, das blank=Truezu Ihren FK-Feldern hinzuzufügen , sodass Ihr ModelForm(entweder benutzerdefiniertes oder vom Administrator generiertes Standardfeld) das Formularfeld erforderlich macht . Auf der Ebene des Datenbankschemas können Sie beide füllen, entweder eine oder keine dieser FKs. Dies wäre in Ordnung, da Sie diese Datenbankfelder nullbar gemacht haben (mit dem null=TrueArgument).
Außerdem (siehe meine anderen Kommentare) möchten Sie möglicherweise überprüfen, ob Ihre FKs wirklich einzigartig sein sollen. Dies macht Ihre Eins-zu-Viele-Beziehung technisch zu einer Eins-zu-Eins-Beziehung. Sie dürfen nur einen einzigen "Inspektions" -Datensatz für eine bestimmte GroupID oder SiteId verwenden (Sie können nicht zwei oder mehr "Inspektionen" für eine GroupId oder SiteId durchführen.) . Wenn dies WIRKLICH das ist, was Sie wollen, können Sie stattdessen ein explizites OneToOneField verwenden (das Datenbankschema ist das gleiche, aber das Modell ist expliziter und der zugehörige Deskriptor ist für diesen Anwendungsfall viel benutzerfreundlicher).
Als Randnotiz: In einem Django-Modell wird ein ForeignKey-Feld als verwandte Modellinstanz und nicht als unformatierte ID angezeigt. IOW, vorausgesetzt:
class Foo(models.Model):
name = models.TextField()
class Bar(models.Model):
foo = models.ForeignKey(Foo)
foo = Foo.objects.create(name="foo")
bar = Bar.objects.create(foo=foo)
dann bar.foowird sich auflösen foo, nicht auf foo.id. Sie möchten also auf jeden Fall Ihre InspectionIDund SiteIDFelder in richtig inspectionund umbenennen site. Übrigens lautet die Namenskonvention in Python 'all_lower_with_underscores' für alles andere als Klassennamen und Pseudokonstanten.
Nun zu Ihrer Kernfrage: Es gibt keine spezielle Standard-SQL-Methode zum Erzwingen einer "die eine oder andere" Einschränkung auf Datenbankebene. Daher wird normalerweise eine CHECK-Einschränkung verwendet , die in einem Django-Modell mit den Meta-Einschränkungen des Modells durchgeführt wird. Option .
Wie Einschränkungen auf DB-Ebene tatsächlich unterstützt und durchgesetzt werden, hängt jedoch von Ihrem DB-Anbieter ab (MySQL <8.0.16 ignoriert sie beispielsweise einfach ), und die Art der Einschränkungen, die Sie hier benötigen, wird im Formular nicht durchgesetzt oder Validierung auf Modellebene , nur wenn Sie versuchen, das Modell zu speichern. Sie möchten also auch eine Validierung entweder auf Modellebene hinzufügen (vorzugsweise) oder Formularebene Validierung, in beiden Fällen in dem (Ltg.) Modell oder clean()Formularmethode.
Um es kurz zu machen:
Überprüfen Sie zunächst, ob Sie dies wirklich möchten unique=True Einschränkung Wenn ja, ersetzen Sie Ihr FK-Feld durch ein OneToOneField.
füge hinzu ein blank=True arg sowohl Ihre FK (oder OneToOne) Felder
- füge das richtige hinzu Check - Einschränkung in Meta Ihrem Modell - die Doc succint ist aber immer noch deutlich genug , wenn Sie wissen , komplexe Abfragen mit dem ORM zu tun (und wenn Sie ihm Zeit , dass du nicht lernen ;-))
- Fügen Sie
clean()Ihrem Modell eine Methode hinzu, die überprüft, ob Sie das eine oder das andere Feld haben, und ansonsten einen Validierungsfehler auslöst
und Sie sollten in Ordnung sein, vorausgesetzt, Ihr RDBMS berücksichtigt natürlich die Überprüfungsbeschränkungen.
Beachten Sie nur, dass Ihr InspectionModell bei diesem Design eine völlig nutzlose (und dennoch kostspielige!) Indirektion ist. Sie erhalten genau dieselben Funktionen zu geringeren Kosten, wenn Sie die FKs (und Einschränkungen, Validierung usw.) direkt in verschiebenInspectionReport .
Jetzt gibt es möglicherweise eine andere Lösung: Behalten Sie das Inspektionsmodell bei, aber platzieren Sie die FK als OneToOneField am anderen Ende der Beziehung (in Site und Gruppe):
class Inspection(models.Model):
id = models.AutoField(primary_key=True) # a pk is always unique !
class InspectionReport(models.Model):
# you actually don't need to manually specify a PK field,
# Django will provide one for you if you don't
# id = models.AutoField(primary_key=True)
inspection = ForeignKey(Inspection, ...)
date = models.DateField(null=True) # you should have a default then
comment = models.CharField(max_length=255, blank=True default="")
signature = models.CharField(max_length=255, blank=True, default="")
class Group(models.Model):
inspection = models.OneToOneField(Inspection, null=True, blank=True)
class Site(models.Model):
inspection = models.OneToOneField(Inspection, null=True, blank=True)
Und dann können Sie alle Berichte für eine bestimmte Site oder Gruppe mit abrufen yoursite.inspection.inspectionreport_set.all() .
Dadurch wird vermieden, dass eine bestimmte Einschränkung oder Validierung hinzugefügt werden muss, jedoch auf Kosten einer zusätzlichen Indirektionsebene (SQL) join Klausel usw.).
Welche dieser Lösungen "die beste" ist, hängt wirklich vom Kontext ab. Sie müssen also die Auswirkungen beider verstehen und überprüfen, wie Sie Ihre Modelle normalerweise verwenden, um herauszufinden, welche für Ihre eigenen Anforderungen besser geeignet ist. Soweit es mich betrifft und ohne mehr Kontext (oder im Zweifel), würde ich die Lösung lieber mit den weniger indirekten Ebenen verwenden, aber mit YMMV.
Hinweis zu generischen Beziehungen: Diese können nützlich sein, wenn Sie wirklich viele mögliche verwandte Modelle haben und / oder vorher nicht wissen, welche Modelle Sie mit Ihren eigenen in Beziehung setzen möchten. Dies ist besonders nützlich für wiederverwendbare Apps (denken Sie an "Kommentare" oder "Tags" usw. Funktionen) oder erweiterbare Apps (Content Management Frameworks usw.). Der Nachteil ist, dass das Abfragen dadurch viel schwieriger wird (und eher unpraktisch ist, wenn Sie manuelle Abfragen für Ihre Datenbank durchführen möchten). Aus Erfahrung können sie schnell zu einem PITA-Bot für Code und Perfs werden. Bewahren Sie sie daher besser auf, wenn es keine bessere Lösung gibt (und / oder wenn der Wartungs- und Laufzeitaufwand kein Problem darstellt).
Meine 2 Cent.
InspectionKlasse erstellen und dann eine Unterklasse inSiteInspectionundGroupInspectionfür die nicht üblichen Teile erstellen.