Django: ¿Por qué algunos campos modelo chocan entre sí?

174

Quiero crear un objeto que contenga 2 enlaces a usuarios. Por ejemplo:

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

pero recibo los siguientes errores al ejecutar el servidor:

  • El descriptor de acceso para el campo 'destino' choca con el campo relacionado 'User.gameclaim_set'. Agregue un argumento related_name a la definición de 'target'.

  • El descriptor de acceso para el campo 'claimer' choca con el campo relacionado 'User.gameclaim_set'. Agregue un argumento related_name a la definición de 'claimer'.

¿Puede explicar por qué recibo los errores y cómo solucionarlos?

Oleg Tarasenko
fuente
Estos mensajes de error son realmente buenos. Ya explican cómo solucionarlos. Y leer sobre ** [ related_nameen la documentación] ** ( docs.djangoproject.com/en/dev/ref/models/fields/#arguments ) explicará por qué ocurren.
Lutz Prechelt

Respuestas:

294

Tienes dos claves foráneas para el usuario. Django crea automáticamente una relación inversa desde el Usuario hasta GameClaim, que generalmente es gameclaim_set. Sin embargo, debido a que tiene dos FK, tendría dos gameclaim_setatributos, lo que obviamente es imposible. Por lo tanto, debe decirle a Django qué nombre usar para la relación inversa.

Use el related_nameatributo en la definición FK. p.ej

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
fuente
49
Buena respuesta, pero no creo que haya logrado evitar la grosería: P El "por qué" no es obvio a menos que sepa cómo funciona internamente django.
Kenny
14
Para alguien que solo está aprendiendo el marco, esto no sería obvio.
jkyle
3
Gracias, el mensaje de error tampoco fue obvio para mí, pero su explicación sobre la relación inversa fue muy útil.
ruquay
1
Solo porque The Clash era una buena banda, no los convierte en un mensaje de error particularmente descriptivo;)
btown
77
También debe mencionarse que si no necesita utilizar las relaciones inversas para todos los modelos. En algunos casos, puede desear que la relación modelo sea unidireccional. En este caso, usa related_name = '+'. Esto le dice a Django que cree una relación unidireccional e ignore la relación inversa.
Tommy Strand
8

El Usermodelo está intentando crear dos campos con el mismo nombre, uno para los GameClaimsque tienen eso Usercomo el target, y otro para los GameClaimsque tienen eso Usercomo el claimer. Aquí están los documentosrelated_name , que es la forma en que Django le permite establecer los nombres de los atributos para que los autogenerados no entren en conflicto.

Hank Gay
fuente
7

El OP no está usando una clase base abstracta ... pero si lo está, encontrará que codificar el nombre_configurado en el FK (por ejemplo ..., nombre_conocido = "mi nombre") dará como resultado varios de estos errores de conflicto. - uno para cada clase heredada de la clase base. El enlace que se proporciona a continuación contiene la solución, que es simple, pero definitivamente no es obvia.

De los documentos de django ...

Si está utilizando el atributo related_name en una ForeignKey o ManyToManyField, siempre debe especificar un nombre inverso único para el campo. Esto normalmente causaría un problema en las clases base abstractas, ya que los campos de esta clase se incluyen en cada una de las clases secundarias, con exactamente los mismos valores para los atributos (incluido el nombre_definido) cada vez.

Más información aquí .

Pascal Polleunus
fuente
2

A veces tiene que usar un formato adicional, en related_name realidad, en cualquier momento cuando se usa la herencia.

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

Aquí no hay conflicto, pero related_name se define una vez y Django se encargará de crear nombres de relación únicos.

luego, en los hijos de la clase Value, tendrá acceso a:

herdboard_height_related
herdboard_lenght_related
herdboard_weight_related
Sławomir Lenart
fuente
0

Parece que me encuentro con esto ocasionalmente cuando agrego un submódulo como aplicación a un proyecto django, por ejemplo, dada la siguiente estructura:

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

Si agrego lo siguiente a INSTALLED_APPS:

'myapp',
'myapp.module',

Django parece procesar el archivo myapp.mymodule models.py dos veces y arroja el error anterior. Esto se puede resolver al no incluir el módulo principal en la lista INSTALLED_APPS:

'myapp.module',

La inclusión de en myapplugar de myapp.modulehace que todas las tablas de la base de datos se creen con nombres incorrectos, por lo que esta parece ser la forma correcta de hacerlo.

Encontré esta publicación mientras buscaba una solución a este problema, así que pensé en poner esto aquí :)

Jordan Hagan
fuente
0

Simplemente agregando a la respuesta de Jordan (gracias por el consejo de Jordan) también puede suceder si importa el nivel por encima de las aplicaciones y luego importa las aplicaciones, por ejemplo

myproject/ apps/ foo_app/ bar_app/

Entonces, si está importando aplicaciones, foo_app y bar_app, entonces podría obtener este problema. Tenía aplicaciones, foo_app y bar_app, todas listadas en la configuración.

Y de todos modos, desea evitar importar aplicaciones, porque entonces tiene la misma aplicación instalada en 2 espacios de nombres diferentes

apps.foo_app y foo_app

lukeaus
fuente