Джанго: Чому деякі моделі моделей стикаються між собою?


174

Я хочу створити об’єкт, який містить 2 посилання на Користувачів. Наприклад:

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

але я отримую такі помилки під час роботи сервера:

  • Accessor for field 'target' зіштовхується із пов’язаним полем 'User.gameclaim_set'. Додайте аргумент related_name до визначення для "target".

  • Пристрій для поля "претендент" зіштовхується із пов'язаним полем "User.gameclaim_set". Додайте аргумент related_name до визначення для "претендента".

Чи можете ви пояснити, чому я отримую помилки та як їх виправити?


Ці повідомлення про помилки справді хороші. Вони вже пояснюють, як їх виправити. І прочитавши ** [ related_nameу документації] ** ( docs.djangoproject.com/en/dev/ref/models/fields/#arguments ) пояснить, чому вони виникають.
Lutz Prechelt

Відповіді:


294

У вас є два сторонні ключі до Користувача. Django автоматично створює зворотне відношення від User назад до GameClaim, як правило gameclaim_set. Однак, оскільки у вас є два FK, ви мали б два gameclaim_setатрибути, що, очевидно, неможливо. Тож вам потрібно сказати Джанго, яке ім'я використовувати для зворотного відношення.

Використовуйте 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 "Чому" не очевидно, якщо ви не знаєте, як джанго працює всередині.
Кенні

14
Для когось, хто тільки вивчає рамки, це не було б очевидним.
jkyle

3
Дякую, повідомлення про помилку мені теж не було очевидним, але ваше пояснення щодо зворотного відношення було дуже корисним.
ruquay

1
Тільки тому, що зіткнення були гарною групою, не робить їх особливо описовим повідомленням про помилку;)
btown

7
Слід також зазначити, що якщо вам не потрібно використовувати зворотні співвідношення для всіх моделей. У деяких випадках ви хочете, щоб відношення моделі було одним із способів. У цьому випадку ви використовуєте related_name = '+'. Це говорить Джанго створити одностороннє відношення і ігнорувати зворотне відношення.
Томмі Странд

8

UserМодель намагається створити два поля з таким же ім'ям, один для GameClaimsяких є , що , Userяк targetі інший для GameClaimsщо є що , Userяк claimer. Ось документи, проrelated_name які Django дозволяє вам встановити імена атрибутів, щоб автогенеровані не конфліктували.


7

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

З документа Джанго ...

Якщо ви використовуєте атрибут 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

І ви хочете уникнути імпортування додатків, оскільки тоді у вас є той самий додаток, встановлений у двох різних просторах імен

apps.foo_app і foo_app

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.