mirror of
https://github.com/django/django.git
synced 2025-10-24 22:26:08 +00:00
Fixed #17673 -- Forbid field shadowing.
Thanks Anssi Kääriäinen for the suggestion.
This commit is contained in:
committed by
Tim Graham
parent
f5123c7291
commit
ee9fcb1672
@@ -1055,8 +1055,12 @@ class Model(six.with_metaclass(ModelBase)):
|
||||
if not cls._meta.swapped:
|
||||
errors.extend(cls._check_fields(**kwargs))
|
||||
errors.extend(cls._check_m2m_through_same_relationship())
|
||||
errors.extend(cls._check_id_field())
|
||||
errors.extend(cls._check_column_name_clashes())
|
||||
clash_errors = cls._check_id_field() + cls._check_field_name_clashes()
|
||||
errors.extend(clash_errors)
|
||||
# If there are field name clashes, hide consequent column name
|
||||
# clashes.
|
||||
if not clash_errors:
|
||||
errors.extend(cls._check_column_name_clashes())
|
||||
errors.extend(cls._check_index_together())
|
||||
errors.extend(cls._check_unique_together())
|
||||
errors.extend(cls._check_ordering())
|
||||
@@ -1175,6 +1179,61 @@ class Model(six.with_metaclass(ModelBase)):
|
||||
else:
|
||||
return []
|
||||
|
||||
@classmethod
|
||||
def _check_field_name_clashes(cls):
|
||||
""" Ref #17673. """
|
||||
|
||||
errors = []
|
||||
used_fields = {} # name or attname -> field
|
||||
|
||||
# Check that multi-inheritance doesn't cause field name shadowing.
|
||||
for parent in cls._meta.parents:
|
||||
for f in parent._meta.local_fields:
|
||||
clash = used_fields.get(f.name) or used_fields.get(f.attname) or None
|
||||
if clash:
|
||||
errors.append(
|
||||
checks.Error(
|
||||
('The field "%s" from parent model '
|
||||
'%s clashes with the field "%s" '
|
||||
'from parent model %s.') % (
|
||||
clash.name, clash.model._meta,
|
||||
f.name, f.model._meta
|
||||
),
|
||||
hint=None,
|
||||
obj=cls,
|
||||
id='E053',
|
||||
)
|
||||
)
|
||||
used_fields[f.name] = f
|
||||
used_fields[f.attname] = f
|
||||
|
||||
# Check that fields defined in the model don't clash with fields from
|
||||
# parents.
|
||||
for f in cls._meta.local_fields:
|
||||
clash = used_fields.get(f.name) or used_fields.get(f.attname) or None
|
||||
# Note that we may detect clash between user-defined non-unique
|
||||
# field "id" and automatically added unique field "id", both
|
||||
# defined at the same model. This special case is considered in
|
||||
# _check_id_field and here we ignore it.
|
||||
id_conflict = (f.name == "id" and
|
||||
clash and clash.name == "id" and clash.model == cls)
|
||||
if clash and not id_conflict:
|
||||
errors.append(
|
||||
checks.Error(
|
||||
('The field clashes with the field "%s" '
|
||||
'from model %s.') % (
|
||||
clash.name, clash.model._meta
|
||||
),
|
||||
hint=None,
|
||||
obj=f,
|
||||
id='E054',
|
||||
)
|
||||
)
|
||||
used_fields[f.name] = f
|
||||
used_fields[f.attname] = f
|
||||
|
||||
return errors
|
||||
|
||||
@classmethod
|
||||
def _check_column_name_clashes(cls):
|
||||
# Store a list of column names which have already been used by other fields.
|
||||
|
||||
@@ -191,8 +191,9 @@ class Field(RegisterLookupMixin):
|
||||
return errors
|
||||
|
||||
def _check_field_name(self):
|
||||
""" Check if field name is valid (i. e. not ending with an underscore).
|
||||
"""
|
||||
""" Check if field name is valid, i.e. 1) does not end with an
|
||||
underscore, 2) does not contain "__" and 3) is not "pk". """
|
||||
|
||||
if self.name.endswith('_'):
|
||||
return [
|
||||
checks.Error(
|
||||
@@ -202,6 +203,24 @@ class Field(RegisterLookupMixin):
|
||||
id='E001',
|
||||
)
|
||||
]
|
||||
elif '__' in self.name:
|
||||
return [
|
||||
checks.Error(
|
||||
'Field names must not contain "__".',
|
||||
hint=None,
|
||||
obj=self,
|
||||
id='E052',
|
||||
)
|
||||
]
|
||||
elif self.name == 'pk':
|
||||
return [
|
||||
checks.Error(
|
||||
'Cannot use "pk" as a field name since it is a reserved name.',
|
||||
hint=None,
|
||||
obj=self,
|
||||
id='E051',
|
||||
)
|
||||
]
|
||||
else:
|
||||
return []
|
||||
|
||||
|
||||
Reference in New Issue
Block a user