Django: Warum kollidieren einige Modellfelder miteinander?

174

Ich möchte ein Objekt erstellen, das 2 Links zu Benutzern enthält. Beispielsweise:

class GameClaim(models.Model):
    target = models.ForeignKey(User)
    claimer = models.ForeignKey(User)
    isAccepted = models.BooleanField()

Beim Ausführen des Servers werden jedoch die folgenden Fehler angezeigt:

  • Der Accessor für das Feld 'Ziel' kollidiert mit dem zugehörigen Feld 'User.gameclaim_set'. Fügen Sie der Definition für 'Ziel' ein Argument für einen verwandten Namen hinzu.

  • Der Accessor für das Feld 'Claimer' kollidiert mit dem zugehörigen Feld 'User.gameclaim_set'. Fügen Sie der Definition für 'Anspruchsberechtigter' ein Argument für einen verwandten Namen hinzu.

Können Sie bitte erklären, warum ich die Fehler erhalte und wie ich sie beheben kann?

Oleg Tarasenko
quelle
Diese Fehlermeldungen sind wirklich gut. Sie erklären bereits, wie man sie repariert. Wenn Sie sich ** [ related_namein der Dokumentation] ** ( docs.djangoproject.com/en/dev/ref/models/fields/#arguments ) ansehen, wird erklärt, warum sie auftreten.
Lutz Prechelt

Antworten:

294

Sie haben zwei Fremdschlüssel für den Benutzer. Django erstellt automatisch eine umgekehrte Beziehung vom Benutzer zurück zu GameClaim, was normalerweise der Fall ist gameclaim_set. Da Sie jedoch zwei FKs haben, würden Sie zwei gameclaim_setAttribute haben, was offensichtlich unmöglich ist. Sie müssen Django also mitteilen, welchen Namen er für die umgekehrte Beziehung verwenden soll.

Verwenden Sie das related_nameAttribut in der FK-Definition. z.B

class GameClaim(models.Model):
    target = models.ForeignKey(User, related_name='gameclaim_targets')
    claimer = models.ForeignKey(User, related_name='gameclaim_users')
    isAccepted = models.BooleanField()
Daniel Roseman
quelle
49
Gute Antwort, aber ich glaube nicht, dass es Ihnen gelungen ist, Unhöflichkeit zu vermeiden: P Das "Warum" ist nicht offensichtlich, es sei denn, Sie wissen, wie Django intern funktioniert.
Kenny
14
Für jemanden, der nur das Framework lernt, wäre dies nicht offensichtlich.
jkyle
3
Danke, die Fehlermeldung war auch für mich nicht offensichtlich, aber Ihre Erklärung zur umgekehrten Beziehung war sehr hilfreich.
Ruquay
1
Nur weil die Clash eine gute Band waren, macht sie keine besonders beschreibende Fehlermeldung;)
Btown
7
Es sollte auch erwähnt werden, dass, wenn Sie nicht die umgekehrten Beziehungen für alle Modelle verwenden müssen. In einigen Fällen möchten Sie möglicherweise, dass die Modellbeziehung eine Möglichkeit ist. In diesem Fall verwenden Sie related_name = '+'. Dies weist Django an, eine Einwegbeziehung zu erstellen und die umgekehrte Beziehung zu ignorieren.
Tommy Strand
8

Das UserModell versucht, zwei Felder mit demselben Namen zu erstellen, eines für GameClaimsdiejenigen, die das Userals das haben target, und eines für GameClaimsdiejenigen, die das Userals das haben claimer. Hier sind die Dokumente zurelated_name Djangos Methode, mit der Sie die Namen der Attribute festlegen können, damit die automatisch generierten Attribute nicht in Konflikt geraten.

Hank Gay
quelle
7

Das OP verwendet keine abstrakte Basisklasse ... aber wenn Sie dies tun, werden Sie feststellen, dass eine harte Codierung des verwandten Namens in der FK (z. B. ... verwandter Name = "mein Name") zu einer Reihe dieser Konfliktfehler führt - eine für jede von der Basisklasse geerbte Klasse. Der unten angegebene Link enthält die Problemumgehung, die einfach, aber definitiv nicht offensichtlich ist.

Aus den Django-Dokumenten ...

Wenn Sie das Attribut related_name für einen ForeignKey oder ManyToManyField verwenden, müssen Sie immer einen eindeutigen umgekehrten Namen für das Feld angeben. Dies würde normalerweise ein Problem in abstrakten Basisklassen verursachen, da die Felder in dieser Klasse in jeder der untergeordneten Klassen enthalten sind und jedes Mal genau dieselben Werte für die Attribute (einschließlich des verwandten Namens) vorliegen.

Mehr Infos hier .

Pascal Polleunus
quelle
2

Manchmal müssen Sie zusätzliche Formatierungen verwenden related_name - eigentlich immer dann, wenn die Vererbung verwendet wird.

class Value(models.Model):
    value = models.DecimalField(decimal_places=2, max_digits=5)
    animal = models.ForeignKey(
        Animal, related_name="%(app_label)s_%(class)s_related")

    class Meta:
        abstract = True

class Height(Value):
    pass

class Weigth(Value):
    pass

class Length(Value):
    pass

Hier gibt es keinen Konflikt, aber related_name wird einmal definiert und Django kümmert sich um die Erstellung eindeutiger Beziehungsnamen.

In Kindern der Value-Klasse haben Sie dann Zugriff auf:

herdboard_height_related
herdboard_lenght_related
herdboard_weight_related
Sławomir Lenart
quelle
0

Dies scheint mir gelegentlich zu begegnen, wenn ich einem Django-Projekt ein Submodul als Anwendung hinzufüge, beispielsweise aufgrund der folgenden Struktur:

myapp/
myapp/module/
myapp/module/models.py

Wenn ich INSTALLED_APPS Folgendes hinzufüge:

'myapp',
'myapp.module',

Django scheint die Datei myapp.mymodule models.py zweimal zu verarbeiten und löst den obigen Fehler aus. Dies kann behoben werden, indem das Hauptmodul nicht in die Liste INSTALLED_APPS aufgenommen wird:

'myapp.module',

Das Einbeziehen von myappanstelle von myapp.modulebewirkt, dass alle Datenbanktabellen mit falschen Namen erstellt werden. Dies scheint also der richtige Weg zu sein.

Ich bin auf diesen Beitrag gestoßen, als ich nach einer Lösung für dieses Problem gesucht habe, also dachte ich mir, ich würde das hier einfügen :)

Jordan Hagan
quelle
0

Wenn Sie nur zu Jordans Antwort hinzufügen (danke für den Tipp Jordan), kann dies auch passieren, wenn Sie die Ebene über den Apps importieren und dann die Apps importieren, z

myproject/ apps/ foo_app/ bar_app/

Wenn Sie also Apps, foo_app und bar_app importieren, kann dieses Problem auftreten. Ich hatte Apps, foo_app und bar_app alle in settings.INSTALLED_APPS aufgeführt

Und Sie möchten den Import von Apps sowieso vermeiden, da dann dieselbe App in zwei verschiedenen Namespaces installiert ist

apps.foo_app und foo_app

lukeaus
quelle