错误消息本身就是一个非常正确的陈述(您省略了输出中给出的错误代码,我认为它是“E303”),但我们需要了解它的实际含义。
我不会问你为什么在这种情况下不使用多对多关系,让我们关注“向后”对象关系是如何工作的:
例子:
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Entry(models.Model):
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
headline = models.CharField(max_length=100)
def __str__(self):
return self.headline
追随“倒退”的关系
一对多关系不以“前向”方式存在,因为它是“多方”持有密钥,但 Django 为关系的“另一方”创建 API 访问器——链接来自相关模型到定义关系的模型。例如,博客对象 b 可以通过 entry_set 属性访问所有相关条目对象的列表:b.entry_set.all()。
...
如果模型具有 ForeignKey,则外键模型的实例将有权访问返回第一个模型的所有实例的 Manager。默认情况下,这个 Manager 被命名为 FOO_set,其中 FOO 是源模型名称,小写。此管理器返回 QuerySet,可以按照上面“检索对象”部分的说明进行过滤和操作。
关注documentation link 了解更多信息/示例。
这是因为这些 API 访问器 'related_name'(用 related_name in Abstract Classes 补充此信息)被实现,以避免字段名和字段名之间的命名冲突
- accessor_name(这将是经理的名字),
和/或
- query_name(即型号名称))
让我们看一下 Django 代码以便更好地理解:
This 是您的错误来源:
def _check_clashes(self):
"""Check accessor and reverse query name clashes."""
from django.db.models.base import ModelBase
errors = []
opts = self.model._meta
# `f.remote_field.model` may be a string instead of a model. Skip if model name is
# not resolved.
if not isinstance(self.remote_field.model, ModelBase):
return []
# Consider that we are checking field `Model.foreign` and the models
# are:
#
# class Target(models.Model):
# model = models.IntegerField()
# model_set = models.IntegerField()
#
# class Model(models.Model):
# foreign = models.ForeignKey(Target)
# m2m = models.ManyToManyField(Target)
# rel_opts.object_name == "Target"
rel_opts = self.remote_field.model._meta
# If the field doesn't install a backward relation on the target model
# (so `is_hidden` returns True), then there are no clashes to check
# and we can skip these fields.
rel_is_hidden = self.remote_field.is_hidden()
rel_name = self.remote_field.get_accessor_name() # i. e. "model_set"
rel_query_name = self.related_query_name() # i. e. "model"
# i.e. "app_label.Model.field".
field_name = '%s.%s' % (opts.label, self.name)
# Check clashes between accessor or reverse query name of `field`
# and any other field name -- i.e. accessor for Model.foreign is
# model_set and it clashes with Target.model_set.
potential_clashes = rel_opts.fields + rel_opts.many_to_many
for clash_field in potential_clashes:
# i.e. "app_label.Target.model_set".
clash_name = '%s.%s' % (rel_opts.label, clash_field.name)
if not rel_is_hidden and clash_field.name == rel_name:
errors.append(
checks.Error(
"Reverse accessor for '%s' clashes with field name '%s'." % (field_name, clash_name),
hint=("Rename field '%s', or add/change a related_name "
"argument to the definition for field '%s'.") % (clash_name, field_name),
obj=self,
id='fields.E302',
)
)
if clash_field.name == rel_query_name:
errors.append(
checks.Error(
"Reverse query name for '%s' clashes with field name '%s'." % (field_name, clash_name),
hint=("Rename field '%s', or add/change a related_name "
"argument to the definition for field '%s'.") % (clash_name, field_name),
obj=self,
id='fields.E303',
)
)
# Check clashes between accessors/reverse query names of `field` and
# any other field accessor -- i. e. Model.foreign accessor clashes with
# Model.m2m accessor.
potential_clashes = (r for r in rel_opts.related_objects if r.field is not self)
for clash_field in potential_clashes:
# i.e. "app_label.Model.m2m".
clash_name = '%s.%s' % (
clash_field.related_model._meta.label,
clash_field.field.name,
)
if not rel_is_hidden and clash_field.get_accessor_name() == rel_name:
errors.append(
checks.Error(
"Reverse accessor for '%s' clashes with reverse accessor for '%s'." % (field_name, clash_name),
hint=("Add or change a related_name argument "
"to the definition for '%s' or '%s'.") % (field_name, clash_name),
obj=self,
id='fields.E304',
)
)
if clash_field.get_accessor_name() == rel_query_name:
errors.append(
checks.Error(
"Reverse query name for '%s' clashes with reverse query name for '%s'."
% (field_name, clash_name),
hint=("Add or change a related_name argument "
"to the definition for '%s' or '%s'.") % (field_name, clash_name),
obj=self,
id='fields.E305',
)
)
return errors
This 是 accessor_name 的诞生方式
def get_accessor_name(self, model=None):
# This method encapsulates the logic that decides what name to give an
# accessor descriptor that retrieves related many-to-one or
# many-to-many objects. It uses the lowercased object_name + "_set",
# but this can be overridden with the "related_name" option. Due to
# backwards compatibility ModelForms need to be able to provide an
# alternate model. See BaseInlineFormSet.get_default_prefix().
opts = model._meta if model else self.related_model._meta
model = model or self.related_model
if self.multiple:
# If this is a symmetrical m2m relation on self, there is no reverse accessor.
if self.symmetrical and model == self.model:
return None
if self.related_name:
return self.related_name
return opts.model_name + ('_set' if self.multiple else '')
这是查询名称(也在related.py中)
def related_query_name(self):
"""
Define the name that can be used to identify this related object in a
table-spanning query.
"""
return self.remote_field.related_query_name or self.remote_field.related_name or self.opts.model_name
现在,您知道命名是如何完成的以及反向关系是什么,您就会明白为什么
“Supplier.default_tariff”的反向查询名称与字段名称“Tariff.supplier”冲突。
以及如何解决:
提示:重命名字段“Tariff.supplier”,或在字段“Supplier.default_tariff”的定义中添加/更改related_name 参数。
希望对您有所帮助。我只是在处理一个相关问题))