Django:为啥有些模型字段会相互冲突?

Posted

技术标签:

【中文标题】Django:为啥有些模型字段会相互冲突?【英文标题】:Django: Why do some model fields *** with each other?Django:为什么有些模型字段会相互冲突? 【发布时间】:2010-11-11 15:48:56 【问题描述】:

我想创建一个包含 2 个指向用户的链接的对象。例如:

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

但在运行服务器时出现以下错误:

字段“target”的访问器与相关字段“User.gameclaim_set”发生冲突。在“target”的定义中添加一个related_name 参数。

字段“claimer”的访问器与相关字段“User.gameclaim_set”发生冲突。为 'claimer' 的定义添加一个 related_name 参数。

您能否解释一下我收到错误的原因以及如何解决这些错误?

【问题讨论】:

这些错误信息非常好。他们已经解释了如何修复它们。阅读 **[related_name in the documentation]**(docs.djangoproject.com/en/dev/ref/models/fields/#arguments) 将解释它们发生的原因。 【参考方案1】:

User 模型正在尝试创建两个具有相同名称的字段,一个用于具有 User 作为 targetGameClaims,另一个用于具有 UserGameClaims作为claimer。这是docs on related_name,这是 Django 让您设置属性名称的方式,因此自动生成的属性不会冲突。

【讨论】:

【参考方案2】:

您有两个 User 的外键。 Django 自动创建从 User 到 GameClaim 的反向关系,通常为gameclaim_set。但是,因为您有两个 FK,所以您将有两个 gameclaim_set 属性,这显然是不可能的。所以你需要告诉 Django 为反向关系使用什么名字。

在 FK 定义中使用 related_name 属性。例如

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

【讨论】:

很好的答案,但我不认为你成功地避免了粗鲁:P 除非你知道 django 内部是如何工作的,否则“为什么”并不明显。 对于刚刚学习框架的人来说,这并不明显。 谢谢,错误信息对我来说也不是很明显,但是您对反向关系的解释非常有帮助。 仅仅因为 *** 是一支优秀的乐队,并不能使它们成为特别具有描述性的错误消息;) 还应该提到,如果您不需要对所有模型使用反向关系。在某些情况下,您可能希望模型关系是一种方式。在这种情况下,您使用related_name='+'。这告诉 Django 创建单向关系并忽略反向关系。【参考方案3】:

OP 没有使用抽象基类...但如果您使用了,您会发现硬编码 FK 中的 related_name(例如 ...,related_name="myname")会导致许多这些冲突错误 - 每个从基类继承的类都有一个。下面提供的链接包含解决方法,它很简单,但绝对不明显。

来自 django 文档...

如果您使用的是related_name ForeignKey 上的属性或 ManyToManyField,你必须总是 指定一个唯一的反向名称 场地。这通常会导致 抽象基类中的问题, 因为这个类的字段是 包含在每个孩子中 类,具有完全相同的值 对于属性(包括 相关名称)。

更多信息here.

【讨论】:

【参考方案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 会导致创建的所有数据库表名称不正确,因此这似乎是正确的方法。

我在寻找这个问题的解决方案时遇到了这篇文章,所以我想把它放在这里:)

【讨论】:

【参考方案5】:

只是添加到乔丹的答案(感谢乔丹的提示),如果您导入应用程序上方的级别然后导入应用程序,例如

myproject/ apps/ foo_app/ bar_app/

因此,如果您正在导入应用程序 foo_app 和 bar_app ,那么您可能会遇到这个问题。我的应用程序 foo_app 和 bar_app 都列在 settings.INSTALLED_APPS

并且无论如何您都希望避免导入应用程序,因为这样您就在 2 个不同的命名空间中安装了同一个应用程序

apps.foo_appfoo_app

【讨论】:

【参考方案6】:

有时您必须在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

【讨论】:

以上是关于Django:为啥有些模型字段会相互冲突?的主要内容,如果未能解决你的问题,请参考以下文章

Django数据模型——通用字段选项

Django数据模型——数据库字段类型

为啥 django 模型的空白和空值不同的选项?

为啥 django 模型的空白和空值不同的选项?

在 Django 中,为啥模型中的 blank=True 不会使表单字段不是强制性的?

我的 Django 模型中的一个字段已经存在于 PostgreSQL 数据库中