diff --git a/django/db/models/base.py b/django/db/models/base.py index c7445672ee0..50a701dce2d 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -94,6 +94,16 @@ class ModelBase(type): new_class._meta.virtual_fields field_names = set([f.name for f in new_fields]) + parent_fields = base._meta.local_fields + base._meta.local_many_to_many + # Check for clashes between locally declared fields and those + # on the base classes (we cannot handle shadowed fields at the + # moment). + for field in parent_fields: + if field.name in field_names: + raise FieldError('Local field %r in class %r clashes ' + 'with field of similar name from ' + 'base class %r' % + (field.name, name, base.__name__)) if not base._meta.abstract: # Concrete classes... if base in o2o_map: @@ -107,16 +117,7 @@ class ModelBase(type): else: # .. and abstract ones. - - # Check for clashes between locally declared fields and those - # on the ABC. - parent_fields = base._meta.local_fields + base._meta.local_many_to_many for field in parent_fields: - if field.name in field_names: - raise FieldError('Local field %r in class %r clashes '\ - 'with field of similar name from '\ - 'abstract base class %r' % \ - (field.name, name, base.__name__)) new_class.add_to_class(field.name, copy.deepcopy(field)) # Pass any non-abstract parent classes onto child. @@ -131,7 +132,8 @@ class ModelBase(type): new_manager = manager._copy_to_model(new_class) new_class.add_to_class(mgr_name, new_manager) - # Inherit virtual fields (like GenericForeignKey) from the parent class + # Inherit virtual fields (like GenericForeignKey) from the parent + # class for field in base._meta.virtual_fields: if base._meta.abstract and field.name in field_names: raise FieldError('Local field %r in class %r clashes '\ diff --git a/docs/topics/db/models.txt b/docs/topics/db/models.txt index 0c87cdc9d19..469733a3b62 100644 --- a/docs/topics/db/models.txt +++ b/docs/topics/db/models.txt @@ -1006,3 +1006,32 @@ field or method to every class that inherits the mix-in. Try to keep your inheritance hierarchies as simple and straightforward as possible so that you won't have to struggle to work out where a particular piece of information is coming from. + +Field name "hiding" is not permitted +------------------------------------- + +In normal Python class inheritance, it is permissible for a child class to +override any attribute from the parent class. In Django, this is not permitted +for attributes that are :class:`~django.db.models.fields.Field` instances (at +least, not at the moment). If a base class has a field called ``author``, you +cannot create another model field called ``author`` in any class that inherits +from that base class. + +Overriding fields in a parent model leads to difficulties in areas such as +initialising new instances (specifying which field is being intialised in +``Model.__init__``) and serialization. These are features which normal Python +class inheritance doesn't have to deal with in quite the same way, so the +difference between Django model inheritance and Python class inheritance isn't +merely arbitrary. + +This restriction only applies to attributes which are +:class:`~django.db.models.fields.Field` instances. Normal Python attributes +can be overridden if you wish. It also only applies to the name of the +attribute as Python sees it: if you are manually specifying the database +column name, you can have the same column name appearing in both a child and +an ancestor model for multi-table inheritance (they are columns in two +different database tables). + +Django will raise a ``FieldError`` exception if you override any model field +in any ancestor model. +