Django: Por que alguns campos de modelo se chocam?

174

Quero criar um objeto que contenha 2 links para usuários. Por exemplo:

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

mas estou recebendo os seguintes erros ao executar o servidor:

  • O acessador do campo 'target' entra em conflito com o campo relacionado 'User.gameclaim_set'. Inclua um argumento related_name na definição de 'target'.

  • O acessador do campo 'reivindicador' entra em conflito com o campo relacionado 'User.gameclaim_set'. Inclua um argumento related_name na definição de 'reivindicador'.

Você pode explicar por que estou recebendo os erros e como corrigi-los?

Oleg Tarasenko
fonte
Essas mensagens de erro são realmente boas. Eles já explicam como corrigi-los. E a leitura de ** [ related_namena documentação] ** ( docs.djangoproject.com/en/dev/ref/models/fields/#arguments ) explica por que elas ocorrem.
Lutz Prechelt 21/09

Respostas:

294

Você tem duas chaves estrangeiras para o usuário. O Django cria automaticamente uma relação inversa entre o Usuário e o GameClaim, o que geralmente é gameclaim_set. No entanto, como você tem dois FKs, você teria dois gameclaim_setatributos, o que é obviamente impossível. Então você precisa dizer ao Django qual nome usar para a relação inversa.

Use o related_nameatributo na definição de FK. por exemplo

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
fonte
49
Boa resposta, mas não acho que você tenha conseguido evitar a grosseria: P O "porquê" não é óbvio, a menos que você esteja ciente de como o django funciona internamente.
Kenny
14
Para alguém que acabou de aprender a estrutura, isso não seria óbvio.
Jkyle 23/02/11
3
Obrigado, a mensagem de erro também não foi óbvia para mim, mas sua explicação sobre a relação inversa foi muito útil.
Ruquay
1
Só porque o Clash eram uma boa banda, não torná-los uma mensagem de erro particularmente descritivo;)
btown
7
Também deve ser mencionado que, se você não precisar usar as relações inversas para todos os modelos. Em alguns casos, você pode desejar que a relação do modelo seja unidirecional. Nesse caso, você usa related_name = '+'. Isto diz ao Django para criar uma relação unidirecional e ignorar a relação inversa.
Tommy Strand
8

O Usermodelo está tentando criar dois campos com o mesmo nome, um para os GameClaimsque têm isso Usercomo o targete outro para os GameClaimsque têm isso Usercomo o claimer. Aqui estão os documentosrelated_name , que é a maneira do Django permitir que você defina os nomes dos atributos para que os gerados automaticamente não entrem em conflito.

Hank Gay
fonte
7

O OP não está usando uma classe base abstrata ... mas, se estiver, você descobrirá que a codificação embutida do related_name no FK (por exemplo ..., related_name = "myname") resultará em vários desses erros de conflito - um para cada classe herdada da classe base. O link fornecido abaixo contém a solução alternativa, que é simples, mas definitivamente não é óbvia.

Dos documentos do django ...

Se você estiver usando o atributo related_name em um ForeignKey ou ManyToManyField, sempre deverá especificar um nome reverso exclusivo para o campo. Isso normalmente causaria um problema nas classes base abstratas, já que os campos nessa classe são incluídos em cada uma das classes filho, com exatamente os mesmos valores para os atributos (incluindo o related_name) a cada vez.

Mais informações aqui .

Pascal Polleunus
fonte
2

Às vezes, você precisa usar formatação extra related_name - na verdade, a qualquer momento em que a herança é usada.

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

Nenhum conflito aqui, mas o related_name é definido uma vez e o Django cuidará da criação de nomes de relações exclusivos.

em filhos da classe Value, você terá acesso a:

herdboard_height_related
herdboard_lenght_related
herdboard_weight_related
Sławomir Lenart
fonte
0

Parece que me deparei com isso ocasionalmente quando adiciono um submódulo como aplicativo a um projeto django, por exemplo, dada a seguinte estrutura:

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

Se eu adicionar o seguinte ao INSTALLED_APPS:

'myapp',
'myapp.module',

O Django parece processar o arquivo myapp.mymodule models.py duas vezes e gera o erro acima. Isso pode ser resolvido não incluindo o módulo principal na lista INSTALLED_APPS:

'myapp.module',

Incluir o em myappvez de myapp.modulefaz com que todas as tabelas do banco de dados sejam criadas com nomes incorretos, portanto essa parece ser a maneira correta de fazer isso.

Me deparei com este post enquanto procurava uma solução para este problema, então pensei em colocar isso aqui :)

Jordan Hagan
fonte
0

Basta adicionar a resposta da Jordan (obrigado pela dica Jordan) também pode acontecer se você importar o nível acima dos aplicativos e depois importar os aplicativos, por exemplo

myproject/ apps/ foo_app/ bar_app/

Portanto, se você estiver importando aplicativos, foo_app e bar_app, poderá obter esse problema. Eu tinha aplicativos, foo_app e bar_app, todos listados nas configurações.INSTALLED_APPS

E você deseja evitar a importação de aplicativos de qualquer maneira, porque você tem o mesmo aplicativo instalado em 2 namespaces diferentes

apps.foo_app e foo_app

lukeaus
fonte