Джанго: Почему некоторые модельные поля сталкиваются друг с другом?

174

Я хочу создать объект, который содержит 2 ссылки на пользователей. Например:

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

но я получаю следующие ошибки при запуске сервера:

  • Аксессор для поля «target» конфликтует со связанным полем «User.gameclaim_set». Добавьте аргумент related_name в определение для «target».

  • Аксессор для поля 'claimer' конфликтует со связанным полем 'User.gameclaim_set'. Добавьте аргумент related_name к определению «claimer».

Не могли бы вы объяснить, почему я получаю ошибки и как их исправить?

Олег тарасенко
источник
Эти сообщения об ошибках действительно хороши. Они уже объясняют, как их исправить. А чтение ** [ related_nameв документации] ** ( docs.djangoproject.com/en/dev/ref/models/fields/#arguments ) объяснит, почему они происходят.
Латц Пречелт

Ответы:

294

У вас есть два внешних ключа для пользователя. Django автоматически создает обратную связь между пользователем и GameClaim, что обычно и происходит gameclaim_set. Однако, поскольку у вас есть два FK, у вас будет два gameclaim_setатрибута, что, очевидно, невозможно. Поэтому вам нужно указать Django, какое имя использовать для обратного отношения.

Используйте related_nameатрибут в определении FK. например

class GameClaim(models.Model):
    target = models.ForeignKey(User, related_name='gameclaim_targets')
    claimer = models.ForeignKey(User, related_name='gameclaim_users')
    isAccepted = models.BooleanField()
Даниэль Роузман
источник
49
Хороший ответ, но я не думаю, что вам удалось избежать грубости: P «Почему» не очевидно, если вы не знаете, как работает django внутри.
Кенни
14
Для кого-то, только изучающего структуру, это не было бы очевидно.
Jkyle
3
Спасибо, сообщение об ошибке не было для меня очевидным, но ваше объяснение обратной связи было очень полезным.
ruquay
1
То, что Clash были хорошей группой, не делает их особенно описательным сообщением об ошибке;)
btown
7
Следует также отметить, что если вам не нужно использовать обратные отношения для всех моделей. В некоторых случаях вы можете пожелать, чтобы отношение модели было односторонним. В этом случае вы используете related_name = '+'. Это говорит Django создать одностороннее отношение и игнорировать обратное отношение.
Томми Стрэнд
8

UserМодель пытается создать два поля с таким же именем, один для GameClaimsкоторых есть , что , Userкак targetи другой для GameClaimsчто есть что , Userкак claimer. Вот документыrelated_name , которые позволяют Django задавать имена атрибутов, чтобы сгенерированные автоматически не конфликтовали.

Хэнк Гей
источник
7

OP не использует абстрактный базовый класс ... но если вы это сделаете, вы обнаружите, что жесткое кодирование related_name в FK (например ..., related_name = "myname") приведет к ряду этих ошибок конфликта - по одному на каждый унаследованный класс от базового класса. Приведенная ниже ссылка содержит обходной путь, который прост, но определенно не очевиден.

Из документов Django ...

Если вы используете атрибут related_name в ForeignKey или ManyToManyField, вы всегда должны указывать уникальное обратное имя для поля. Это обычно вызывает проблемы в абстрактных базовых классах, поскольку поля этого класса включаются в каждый из дочерних классов, каждый раз с абсолютно одинаковыми значениями для атрибутов (включая related_name).

Больше информации здесь .

Паскаль Поллеунус
источник
2

Иногда вам нужно использовать дополнительное форматирование related_name - фактически, в любое время, когда используется наследование.

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

Здесь нет конфликта, но related_name определяется один раз, и Django позаботится о создании уникальных имен отношений.

тогда у детей класса Value у вас будет доступ к:

herdboard_height_related
herdboard_lenght_related
herdboard_weight_related
Славомир Ленарт
источник
0

Кажется, я иногда сталкиваюсь с этим, когда добавляю подмодуль в качестве приложения в проект django, например, учитывая следующую структуру:

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

Если я добавлю следующее в INSTALLED_APPS:

'myapp',
'myapp.module',

Кажется, Django дважды обрабатывает файл myapp.mymodule models.py и выдает вышеуказанную ошибку. Это можно решить, не включив основной модуль в список INSTALLED_APPS:

'myapp.module',

Включение myappвместо вместо myapp.moduleприводит к тому, что все таблицы базы данных создаются с неправильными именами, так что, похоже, это правильный путь.

Я наткнулся на этот пост, когда искал решение этой проблемы, так что решил, что я бы поставил это здесь

Джордан Хаган
источник
0

Просто добавив к ответу Джордана (спасибо за подсказку Джордан), это также может произойти, если вы импортируете уровень выше приложений, а затем импортируете приложения, например

myproject/ apps/ foo_app/ bar_app/

Так что, если вы импортируете приложения, foo_app и bar_app, вы можете получить эту проблему. У меня были приложения, foo_app и bar_app, все они перечислены в настройках. INSTALLED_APPS

И вы все равно хотите избегать импорта приложений, потому что тогда у вас одно и то же приложение установлено в 2 разных пространствах имен

apps.foo_app и foo_app

lukeaus
источник