Django: Pourquoi certains champs de modèle sont-ils en conflit les uns avec les autres?


174

Je souhaite créer un objet contenant 2 liens vers des utilisateurs. Par exemple:

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

mais j'obtiens les erreurs suivantes lors de l'exécution du serveur:

  • L'accesseur du champ «cible» entre en conflit avec le champ associé «User.gameclaim_set». Ajoutez un argument related_name à la définition de «cible».

  • L'accesseur du champ «claimer» est en conflit avec le champ associé «User.gameclaim_set». Ajoutez un argument related_name à la définition de 'claimer'.

Pouvez-vous s'il vous plaît expliquer pourquoi j'obtiens les erreurs et comment les corriger?


Ces messages d'erreur sont vraiment bons. Ils expliquent déjà comment les réparer. Et la lecture sur ** [ related_namedans la documentation] ** ( docs.djangoproject.com/en/dev/ref/models/fields/#arguments ) expliquera pourquoi ils se produisent.
Lutz Prechelt

Réponses:


294

Vous avez deux clés étrangères pour l'utilisateur. Django crée automatiquement une relation inverse entre l'utilisateur et GameClaim, ce qui est généralement le cas gameclaim_set. Cependant, comme vous avez deux FK, vous en auriez deuxgameclaim_set attributs, ce qui est évidemment impossible. Vous devez donc indiquer à Django le nom à utiliser pour la relation inverse.

Utilisez l' related_nameattribut dans la définition FK. par exemple

class GameClaim(models.Model):
    target = models.ForeignKey(User, related_name='gameclaim_targets')
    claimer = models.ForeignKey(User, related_name='gameclaim_users')
    isAccepted = models.BooleanField()

49
Bonne réponse, mais je ne pense pas que vous ayez réussi à éviter l'impolitesse: P Le "pourquoi" n'est pas évident à moins que vous ne sachiez comment django fonctionne en interne.
Kenny

14
Pour quelqu'un qui apprend juste le cadre, ce ne serait pas évident.
jkyle

3
Merci, le message d'erreur n'était pas non plus évident pour moi, mais votre explication sur la relation inverse a été très utile.
ruquay

1
Ce n'est pas parce que les Clash étaient un bon groupe que cela en fait un message d'erreur particulièrement descriptif;)
btown

7
Il convient également de mentionner que si vous n'avez pas besoin d'utiliser les relations inverses pour tous les modèles. Dans certains cas, vous souhaiterez peut-être que la relation de modèle soit à sens unique. Dans ce cas, vous utilisez related_name = '+'. Cela indique à Django de créer une relation à sens unique et d'ignorer la relation inverse.
Tommy Strand

8

Le Usermodèle essaie de créer deux champs avec le même nom, un pour les GameClaimsqui ont cela Usercomme le target, et un autre pour les GameClaimsqui ont cela Usercomme le claimer. Voici la documentation surrelated_name , qui est la manière de Django de vous permettre de définir les noms des attributs afin que ceux générés automatiquement ne soient pas en conflit.


7

L'OP n'utilise pas de classe de base abstraite ... mais si vous l'êtes, vous constaterez que le codage en dur du nom_relié dans le FK (par exemple ..., related_name = "myname") entraînera un certain nombre de ces erreurs de conflit - un pour chaque classe héritée de la classe de base. Le lien fourni ci-dessous contient la solution de contournement, qui est simple, mais certainement pas évidente.

À partir de la documentation django ...

Si vous utilisez l'attribut related_name sur un ForeignKey ou ManyToManyField, vous devez toujours spécifier un nom inverse unique pour le champ. Cela poserait normalement un problème dans les classes de base abstraites, puisque les champs de cette classe sont inclus dans chacune des classes enfants, avec exactement les mêmes valeurs pour les attributs (y compris related_name) à chaque fois.

Plus d'infos ici .


2

Parfois, vous devez utiliser un formatage supplémentaire related_name - en fait, à chaque fois que l'héritage est utilisé.

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

Pas de conflit ici, mais related_name est défini une fois et Django se chargera de créer des noms de relation uniques.

puis dans les enfants de la classe Value, vous aurez accès à:

herdboard_height_related
herdboard_lenght_related
herdboard_weight_related

0

Il semble que je rencontre cela occasionnellement lorsque j'ajoute un sous-module en tant qu'application à un projet django, par exemple compte tenu de la structure suivante:

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

Si j'ajoute ce qui suit à INSTALLED_APPS:

'myapp',
'myapp.module',

Django semble traiter le fichier myapp.mymodule models.py deux fois et renvoie l'erreur ci-dessus. Cela peut être résolu en n'incluant pas le module principal dans la liste INSTALLED_APPS:

'myapp.module',

Inclure le myappau lieu de myapp.moduleentraîne la création de toutes les tables de la base de données avec des noms incorrects, donc cela semble être la bonne façon de le faire.

Je suis tombé sur ce post en cherchant une solution à ce problème, alors je me suis dit que je mettrais ça ici :)


0

En ajoutant simplement à la réponse de Jordan (merci pour le conseil Jordan), cela peut également arriver si vous importez le niveau au-dessus des applications, puis importez les applications, par exemple

myproject/ apps/ foo_app/ bar_app/

Donc, si vous importez des applications, foo_app et bar_app, vous pourriez avoir ce problème. J'avais des applications, foo_app et bar_app, toutes répertoriées dans les paramètres.INSTALLED_APPS

Et vous voulez quand même éviter d'importer des applications, car alors vous avez la même application installée dans 2 espaces de noms différents

apps.foo_app et foo_app

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.