Mein Problem ist, dass ich ein Modell habe, das einen von zwei Fremdschlüsseln verwenden kann, um zu sagen, um welche Art von Modell es sich handelt. Ich möchte, dass es mindestens einen braucht, aber nicht beide. Kann ich dieses Modell noch als ein Modell haben oder sollte ich es in zwei Typen aufteilen? Hier ist der Code:
class Inspection(models.Model):
InspectionID = models.AutoField(primary_key=True, unique=True)
GroupID = models.ForeignKey('PartGroup', on_delete=models.CASCADE, null=True, unique=True)
SiteID = models.ForeignKey('Site', on_delete=models.CASCADE, null=True, unique=True)
@classmethod
def create(cls, groupid, siteid):
inspection = cls(GroupID = groupid, SiteID = siteid)
return inspection
def __str__(self):
return str(self.InspectionID)
class InspectionReport(models.Model):
ReportID = models.AutoField(primary_key=True, unique=True)
InspectionID = models.ForeignKey('Inspection', on_delete=models.CASCADE, null=True)
Date = models.DateField(auto_now=False, auto_now_add=False, null=True)
Comment = models.CharField(max_length=255, blank=True)
Signature = models.CharField(max_length=255, blank=True)
Das Problem ist das Inspection
Modell. Dies sollte entweder mit einer Gruppe oder einer Site verknüpft sein, jedoch nicht mit beiden. Derzeit benötigt dieses Setup beides.
Ich hätte lieber nicht aufgeteilt in zwei fast identische Modelle GroupInspection
und SiteInspection
, so dass jede Lösung , die es als ein Modell hält wäre ideal.
django
django-models
CalMac
quelle
quelle
Inspection
Klasse erstellen und dann eine Unterklasse inSiteInspection
undGroupInspection
für die nicht üblichen Teile erstellen.unique=True
Teil in Ihren FK-Feldern bedeutet, dass nur eineInspection
Instanz für eine bestimmteGroupID
oderSiteID
Instanz existieren kann - IOW, es ist eine Eins-zu-Eins-Beziehung, keine Eins-zu-Viele-Beziehung. Willst du das wirklich?Inspection
eine Verbindung zwischen demGroup
oderSite
und einem zu habenInspectionID
, dann kann ich mehrere "Inspektionen" in Form vonInspectionReport
für diese eine Beziehung durchführen. Dies wurde gemacht, damit ich leichterDate
nach allen Datensätzen sortieren kann, die sich auf einenGroup
oder mehrere beziehenSite
. Hoffe das macht SinnAntworten:
Ich würde vorschlagen, dass Sie eine solche Validierung nach Django-Art durchführen
durch Überschreiben der
clean
Methode des Django-Modellsquelle
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=True
zu Ihren FK-Feldern hinzuzufügen , sodass IhrModelForm
(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 demnull=True
Argument).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:
dann
bar.foo
wird sich auflösenfoo
, nicht auffoo.id
. Sie möchten also auf jeden Fall IhreInspectionID
undSiteID
Felder in richtiginspection
und umbenennensite
. Ü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) Felderclean()
Ihrem Modell eine Methode hinzu, die überprüft, ob Sie das eine oder das andere Feld haben, und ansonsten einen Validierungsfehler auslöstund Sie sollten in Ordnung sein, vorausgesetzt, Ihr RDBMS berücksichtigt natürlich die Überprüfungsbeschränkungen.
Beachten Sie nur, dass Ihr
Inspection
Modell 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):
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.
quelle
Django verfügt über eine neue (seit 2.2) Schnittstelle zum Erstellen von DB-Einschränkungen: https://docs.djangoproject.com/de/3.0/ref/models/constraints/
Sie können a verwenden
CheckConstraint
, um zu erzwingen, dass one-and-only-one nicht null ist. Ich benutze zwei aus Gründen der Klarheit:Dadurch wird die Einschränkung nur auf DB-Ebene erzwungen. Sie müssen Eingaben in Ihren Formularen oder Serialisierern manuell validieren.
Als Randnotiz sollten Sie wahrscheinlich
OneToOneField
anstelle von verwendenForeignKey(unique=True)
. Du wirst auch wollenblank=True
.quelle
Ich denke, Sie sprechen über generische Beziehungen , Dokumente . Ihre Antwort sieht ähnlich diesem .
Vor einiger Zeit musste ich generische Beziehungen verwenden, aber ich las in einem Buch und woanders, dass die Verwendung vermieden werden sollte. Ich denke, es waren zwei Kugeln Django.
Am Ende habe ich ein Modell wie dieses erstellt:
Ich bin mir nicht sicher, ob es eine gute Lösung ist, und wie Sie bereits erwähnt haben, möchten Sie sie lieber nicht verwenden, aber dies funktioniert in meinem Fall.
quelle
Es könnte spät sein, Ihre Frage zu beantworten, aber ich dachte, meine Lösung könnte zum Fall einer anderen Person passen.
Ich würde ein neues Modell erstellen, es nennen
Dependency
und die Logik in diesem Modell anwenden.Dann würde ich die Logik so schreiben, dass sie sehr explizit anwendbar ist.
Jetzt müssen Sie nur noch eine Single
ForeignKey
für Ihre erstellenInspection
Modell .In Ihren
view
Funktionen müssen Sie einDependency
Objekt erstellen und es dann IhremInspection
Datensatz zuweisen . Stellen Sie sicher, dass Siecreate_dependency_object
in Ihrem verwendenview
Funktionen verwenden.Dies macht Ihren Code ziemlich explizit und fehlerfrei. Die Durchsetzung kann zu leicht umgangen werden. Der Punkt ist jedoch, dass Vorkenntnisse erforderlich sind, um diese genaue Einschränkung zu umgehen.
quelle