问题:Django:为什么某些模型字段会相互冲突?
我想创建一个包含2个指向用户的链接的对象。例如:
class GameClaim(models.Model):
target = models.ForeignKey(User)
claimer = models.ForeignKey(User)
isAccepted = models.BooleanField()
但是运行服务器时出现以下错误:
字段“目标”的访问器与相关字段“ User.gameclaim_set”冲突。在’target’的定义中添加related_name参数。
字段“ claimer”的访问器与相关字段“ User.gameclaim_set”冲突。在“ claimer”的定义中添加一个related_name参数。
您能否解释为什么我会收到错误以及如何解决这些错误?
I want to create an object that contains 2 links to Users. For example:
class GameClaim(models.Model):
target = models.ForeignKey(User)
claimer = models.ForeignKey(User)
isAccepted = models.BooleanField()
but I am getting the following errors when running the server:
Accessor for field ‘target’ clashes with related field ‘User.gameclaim_set’. Add a related_name argument to the definition for ‘target’.
Accessor for field ‘claimer’ clashes with related field ‘User.gameclaim_set’. Add a related_name argument to the definition for ‘claimer’.
Can you please explain why I am getting the errors and how to fix them?
回答 0
您有两个用户外键。Django自动创建一个从User到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()
You have two foreign keys to User. Django automatically creates a reverse relation from User back to GameClaim, which is usually gameclaim_set
. However, because you have two FKs, you would have two gameclaim_set
attributes, which is obviously impossible. So you need to tell Django what name to use for the reverse relation.
Use the related_name
attribute in the FK definition. e.g.
class GameClaim(models.Model):
target = models.ForeignKey(User, related_name='gameclaim_targets')
claimer = models.ForeignKey(User, related_name='gameclaim_users')
isAccepted = models.BooleanField()
回答 1
该User
模型试图创建具有相同名称的两个领域,一个是GameClaims
说有User
作为target
,而另一个用于GameClaims
该有User
作为claimer
。这是上的文档related_name
,这是Django允许您设置属性名称的方法,以便自动生成的属性不会冲突。
The User
model is trying to create two fields with the same name, one for the GameClaims
that have that User
as the target
, and another for the GameClaims
that have that User
as the claimer
. Here’s the docs on related_name
, which is Django’s way of letting you set the names of the attributes so the autogenerated ones don’t conflict.
回答 2
OP没有使用抽象基类…但是,如果您使用的是,您会发现在FK中硬编码related_name(例如…,related_name =“ myname”)会导致许多此类冲突错误-从基类继承的每个类一个。下面提供的链接包含解决方法,这很简单,但绝对不明显。
从Django文档…
如果在ForeignKey或ManyToManyField上使用related_name属性,则必须始终为该字段指定唯一的反向名称。这通常会在抽象基类中引起问题,因为此类的字段包含在每个子类中,并且每次属性(包括related_name)的值都完全相同。
更多信息在这里。
The OP isn’t using a abstract base class… but if you are, you will find that hard coding the related_name in the FK (e.g. …, related_name=”myname”) will result in a number of these conflict errors – one for each inherited class from the base class. The link provided below contains the workaround, which is simple, but definitely not obvious.
From the django docs…
If you are using the related_name
attribute on a ForeignKey or
ManyToManyField, you must always
specify a unique reverse name for the
field. This would normally cause a
problem in abstract base classes,
since the fields on this class are
included into each of the child
classes, with exactly the same values
for the attributes (including
related_name) each time.
More info here.
回答 3
有时,您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
Sometimes you have to use extra formatting in related_name
– actually, any time when inheritance is used.
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
No clash here, but related_name is defined once and Django will take care for creating unique relation names.
then in children of Value class, you’ll have access to:
herdboard_height_related
herdboard_lenght_related
herdboard_weight_related
回答 4
当我将子模块作为应用程序添加到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
会导致所有数据库表都使用不正确的名称创建,因此这似乎是正确的方法。
我在寻找此问题的解决方案时遇到了这篇文章,所以我想把它放在这里:)
I seem to come across this occasionally when I add a submodule as an application to a django project, for example given the following structure:
myapp/
myapp/module/
myapp/module/models.py
If I add the following to INSTALLED_APPS:
'myapp',
'myapp.module',
Django seems to process the myapp.mymodule models.py file twice and throws the above error. This can be resolved by not including the main module in the INSTALLED_APPS list:
'myapp.module',
Including the myapp
instead of myapp.module
causes all the database tables to be created with incorrect names, so this seems to be the correct way to do it.
I came across this post while looking for a solution to this problem so figured I’d put this here :)
回答 5
只是添加约旦的答案(感谢约旦的提示),如果您在应用程序上方导入级别,然后导入应用程序,则也可能发生这种情况,例如
myproject/
apps/
foo_app/
bar_app/
因此,如果要导入应用程序foo_app和bar_app,则可能会遇到此问题。我的应用程序foo_app和bar_app都列在设置中。INSTALLED_APPS
而且您还是想避免导入应用程序,因为那样您会将相同的应用程序安装在2个不同的命名空间中
apps.foo_app
和
foo_app
Just adding to Jordan’s answer (thanks for the tip Jordan) it can also happen if you import the level above the apps and then import the apps e.g.
myproject/
apps/
foo_app/
bar_app/
So if you are importing apps, foo_app and bar_app then you could get this issue. I had apps, foo_app and bar_app all listed in settings.INSTALLED_APPS
And you want to avoid importing apps anyway, because then you have the same app installed in 2 different namespaces
apps.foo_app
and
foo_app