Django: Dlaczego niektóre pola modelu kolidują ze sobą?

174

Chcę utworzyć obiekt zawierający 2 linki do użytkowników. Na przykład:

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

ale otrzymuję następujące błędy podczas uruchamiania serwera:

  • Akcesor dla pola „target” koliduje z polem pokrewnym „User.gameclaim_set”. Dodaj argument related_name do definicji „celu”.

  • Akcesor dla pola „wnioskodawca” koliduje z pokrewnym polem „User.gameclaim_set”. Dodaj argument related_name do definicji „wnioskodawcy”.

Czy możesz wyjaśnić, dlaczego otrzymuję błędy i jak je naprawić?

Oleg Tarasenko
źródło
Te komunikaty o błędach są naprawdę dobre. Już wyjaśniają, jak je naprawić. Lektura ** [ related_namew dokumentacji] ** ( docs.djangoproject.com/en/dev/ref/models/fields/#arguments ) wyjaśni, dlaczego się pojawiają.
Lutz Prechelt

Odpowiedzi:

294

Masz dwa klucze obce dla użytkownika. Django automatycznie tworzy odwrotną relację z User z powrotem do GameClaim, co zwykle jest gameclaim_set. Jednakże, ponieważ masz dwa FK, miałbyś dwa gameclaim_setatrybuty, co jest oczywiście niemożliwe. Musisz więc powiedzieć Django, jakiej nazwy użyć dla relacji odwrotnej.

Użyj related_nameatrybutu w definicji FK. na przykład

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
źródło
49
Dobra odpowiedź, ale nie sądzę, żeby udało ci się uniknąć chamstwa: P „Dlaczego” nie jest oczywiste, chyba że jesteś świadomy, jak django działa wewnętrznie.
Kenny,
14
Dla kogoś, kto dopiero poznaje ramy, nie byłoby to oczywiste.
jkyle
3
Dzięki, komunikat o błędzie również nie był dla mnie oczywisty, ale Twoje wyjaśnienie dotyczące odwrotnej relacji było bardzo pomocne.
ruquay
1
Tylko dlatego, że Clash byli dobrym zespołem, nie czyni z nich szczególnie opisowego komunikatu o błędzie;)
btown
7
Należy również wspomnieć, że jeśli nie musisz używać relacji odwrotnych dla wszystkich modeli. W niektórych przypadkach możesz chcieć, aby relacja modelu była jednokierunkowa. W tym przypadku używasz related_name = „+”. To mówi Django, aby utworzył relację jednokierunkową i zignorował relację odwrotną.
Tommy Strand
8

UserModelu próbuje utworzyć dwa pola o tej samej nazwie, po jednym dla GameClaimsże ma to Userjak target, a inna dla GameClaimsże ma to Userjak claimer. Oto dokumentacjarelated_name , czyli sposób, w jaki Django pozwala ci ustawić nazwy atrybutów, aby te wygenerowane automatycznie nie powodowały konfliktów.

Hank Gay
źródło
7

OP nie używa abstrakcyjnej klasy bazowej ... ale jeśli tak, przekonasz się, że na stałe zakodowanie powiązanej_nazwy w FK (np. ..., nazwa_powiązanej = "moja_nazwa") spowoduje szereg takich błędów konfliktu - po jednym dla każdej klasy odziedziczonej z klasy bazowej. Poniższy link zawiera obejście, które jest proste, ale zdecydowanie nieoczywiste.

Z dokumentacji django ...

Jeśli używasz atrybutu related_name w ForeignKey lub ManyToManyField, zawsze musisz określić unikalną odwrotną nazwę pola. Zwykle powodowałoby to problem w abstrakcyjnych klasach bazowych, ponieważ pola tej klasy są zawarte w każdej z klas potomnych, za każdym razem z dokładnie takimi samymi wartościami atrybutów (w tym nazwa_powiązanej).

Więcej informacji tutaj .

Pascal Polleunus
źródło
2

Czasami musisz użyć dodatkowego formatowania related_name - właściwie zawsze, gdy używane jest dziedziczenie.

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

Nie ma tu konfliktu, ale related_name jest zdefiniowane raz i Django zajmie się tworzeniem unikalnych nazw relacji.

wtedy w dzieciach klasy Value będziesz mieć dostęp do:

herdboard_height_related
herdboard_lenght_related
herdboard_weight_related
Sławomir Lenart
źródło
0

Wydaje się, że spotykam się z tym czasami, gdy dodaję podmoduł jako aplikację do projektu django, na przykład biorąc pod uwagę następującą strukturę:

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

Jeśli dodam następujące elementy do INSTALLED_APPS:

'myapp',
'myapp.module',

Django wydaje się dwukrotnie przetwarzać plik myapp.mymodule models.py i zgłasza powyższy błąd. Można to rozwiązać, nie umieszczając głównego modułu na liście INSTALLED_APPS:

'myapp.module',

Włączenie opcji myappzamiast myapp.modulepowoduje, że wszystkie tabele bazy danych zostaną utworzone z niepoprawnymi nazwami, więc wydaje się, że jest to właściwy sposób zrobienia tego.

Natknąłem się na ten post, szukając rozwiązania tego problemu, więc pomyślałem, że wstawię to tutaj :)

Jordan Hagan
źródło
0

Po prostu dodając do odpowiedzi Jordana (dzięki za wskazówkę Jordan) może się to również zdarzyć, jeśli zaimportujesz poziom powyżej aplikacji, a następnie zaimportujesz aplikacje, np.

myproject/ apps/ foo_app/ bar_app/

Więc jeśli importujesz aplikacje, foo_app i bar_app, możesz napotkać ten problem. Miałem aplikacje, foo_app i bar_app wszystkie wymienione w ustawieniach.INSTALLED_APPS

I tak chcesz uniknąć importowania aplikacji, ponieważ masz wtedy tę samą aplikację zainstalowaną w 2 różnych przestrzeniach nazw

apps.foo_app i foo_app

lukeaus
źródło