Skip to content

Commit

Permalink
[1.0.X] Made it explicit if you accidentally override a Field from a …
Browse files Browse the repository at this point in the history
…parent model.

This was always not working reliably (model initialization and serialization
were two of the problems). Now, it's an explicit error. Also, documented.

Fixed #10252.

Backport of r9974 from trunk.

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.0.X@9975 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information
malcolmt committed Mar 4, 2009
1 parent 182301c commit 79f53b4
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 10 deletions.
22 changes: 12 additions & 10 deletions django/db/models/base.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -94,6 +94,16 @@ def __new__(cls, name, bases, attrs):
new_class._meta.virtual_fields new_class._meta.virtual_fields
field_names = set([f.name for f in new_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: if not base._meta.abstract:
# Concrete classes... # Concrete classes...
if base in o2o_map: if base in o2o_map:
Expand All @@ -107,16 +117,7 @@ def __new__(cls, name, bases, attrs):


else: else:
# .. and abstract ones. # .. 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: 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)) new_class.add_to_class(field.name, copy.deepcopy(field))


# Pass any non-abstract parent classes onto child. # Pass any non-abstract parent classes onto child.
Expand All @@ -131,7 +132,8 @@ def __new__(cls, name, bases, attrs):
new_manager = manager._copy_to_model(new_class) new_manager = manager._copy_to_model(new_class)
new_class.add_to_class(mgr_name, new_manager) 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: for field in base._meta.virtual_fields:
if base._meta.abstract and field.name in field_names: if base._meta.abstract and field.name in field_names:
raise FieldError('Local field %r in class %r clashes '\ raise FieldError('Local field %r in class %r clashes '\
Expand Down
29 changes: 29 additions & 0 deletions docs/topics/db/models.txt
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -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 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 won't have to struggle to work out where a particular piece of information is
coming from. 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.

0 comments on commit 79f53b4

Please sign in to comment.