diff options
| author | Christopher Medrela <chris.medrela@gmail.com> | 2014-02-07 22:34:56 +0100 |
|---|---|---|
| committer | Tim Graham <timograham@gmail.com> | 2014-02-10 10:04:19 -0500 |
| commit | ee9fcb1672ddf5910ed8c45c37a00f32ebbe2bb1 (patch) | |
| tree | 5032ef2dc44c9c50b251adeb3506530e93e147e0 /django | |
| parent | f5123c7291e855713b59e739bad0e50a1d50d3ef (diff) | |
Fixed #17673 -- Forbid field shadowing.
Thanks Anssi Kääriäinen for the suggestion.
Diffstat (limited to 'django')
| -rw-r--r-- | django/db/models/base.py | 63 | ||||
| -rw-r--r-- | django/db/models/fields/__init__.py | 23 |
2 files changed, 82 insertions, 4 deletions
diff --git a/django/db/models/base.py b/django/db/models/base.py index 4b8aededa9..6270e35b37 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -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()) @@ -1176,6 +1180,61 @@ class Model(six.with_metaclass(ModelBase)): 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. used_column_names = [] diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py index 4454429997..77a48d0723 100644 --- a/django/db/models/fields/__init__.py +++ b/django/db/models/fields/__init__.py @@ -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 [] |
