这个错误信息本身已经相当正确了(你忽略了输出中给出的错误代码,我想它是'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访问器。例如,Blog对象b可以通过entry_set属性访问所有相关Entry对象的列表:b.entry_set.all()
。
...
如果一个模型具有ForeignKey,则外键模型的实例将具有返回第一个模型的所有实例的Manager 的访问权限。默认情况下,此Manager的名称为FOO_set,其中FOO是源模型名称的小写形式。此Manager返回QuerySets,可以按照“检索对象”部分中所述进行过滤和操作。
单击文档链接获取更多信息/示例。
正是由于这些API访问器,'related_name'(与抽象类中的related_name 配合使用)被实现,以避免字段名称与
- 访问器名称(这将是Manager的名称)之间的名称冲突, 和/或
- 查询名称(即模型名称)
让我们查看Django代码以更好地理解:
这里是您发生错误的位置:
def _check_clashes(self):
"""Check accessor and reverse query name clashes."""
from django.db.models.base import ModelBase
errors = []
opts = self.model._meta
if not isinstance(self.remote_field.model, ModelBase):
return []
rel_opts = self.remote_field.model._meta
rel_is_hidden = self.remote_field.is_hidden()
rel_name = self.remote_field.get_accessor_name()
rel_query_name = self.related_query_name()
field_name = '%s.%s' % (opts.label, self.name)
potential_clashes = rel_opts.fields + rel_opts.many_to_many
for clash_field in potential_clashes:
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',
)
)
potential_clashes = (r for r in rel_opts.related_objects if r.field is not self)
for clash_field in potential_clashes:
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
这里是生成accessor_name的方法。
def get_accessor_name(self, model=None):
opts = model._meta if model else self.related_model._meta
model = model or self.related_model
if self.multiple:
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'字段,或添加/更改related_name参数以用于'Supplier.default_tariff'字段定义。
希望这有所帮助。我刚处理完一个相关的问题))
related_name="+"
是什么意思?除了修复错误之外,会发生什么?哪些功能会停止工作? - Toskanrelated_name="+"
时,Django不会创建反向关系。在这种情况下,Tariff
将不会与Supplier
模型有反向关系。 - maczos