1
0
mirror of https://github.com/django/django.git synced 2025-07-05 18:29:11 +00:00

queryset-refactor: Added the ability to manually specify a child-parent link.

git-svn-id: http://code.djangoproject.com/svn/django/branches/queryset-refactor@7142 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Malcolm Tredinnick 2008-02-22 04:58:28 +00:00
parent 0c20e88e65
commit b7be3d63e3
5 changed files with 85 additions and 38 deletions

View File

@ -59,21 +59,6 @@ class ModelBase(type):
if not hasattr(meta, 'get_latest_by'):
new_class._meta.get_latest_by = base_meta.get_latest_by
# Do the appropriate setup for any model parents.
abstract_parents = []
for base in parents:
if not hasattr(base, '_meta'):
# Things without _meta aren't functional models, so they're
# uninteresting parents.
continue
if not base._meta.abstract:
attr_name = '%s_ptr' % base._meta.module_name
field = OneToOneField(base, name=attr_name, auto_created=True)
new_class.add_to_class(attr_name, field)
new_class._meta.parents[base] = field
else:
abstract_parents.append(base)
if getattr(new_class, '_default_manager', None) is not None:
# We have a parent who set the default manager. We need to override
# this.
@ -93,13 +78,32 @@ class ModelBase(type):
for obj_name, obj in attrs.items():
new_class.add_to_class(obj_name, obj)
for parent in abstract_parents:
names = [f.name for f in new_class._meta.local_fields + new_class._meta.many_to_many]
for field in parent._meta.local_fields:
if field.name in names:
raise TypeError('Local field %r in class %r clashes with field of similar name from abstract base class %r'
% (field.name, name, parent.__name__))
new_class.add_to_class(field.name, field)
# Do the appropriate setup for any model parents.
o2o_map = dict([(f.rel.to, f) for f in new_class._meta.local_fields
if isinstance(f, OneToOneField)])
for base in parents:
if not hasattr(base, '_meta'):
# Things without _meta aren't functional models, so they're
# uninteresting parents.
continue
if not base._meta.abstract:
if base in o2o_map:
field = o2o_map[base]
field.primary_key = True
new_class._meta.setup_pk(field)
else:
attr_name = '%s_ptr' % base._meta.module_name
field = OneToOneField(base, name=attr_name,
auto_created=True, parent_link=True)
new_class.add_to_class(attr_name, field)
new_class._meta.parents[base] = field
else:
names = [f.name for f in new_class._meta.local_fields + new_class._meta.many_to_many]
for field in base._meta.local_fields:
if field.name in names:
raise TypeError('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, field)
if abstract:
# Abstract base models can't be instantiated and don't appear in

View File

@ -461,8 +461,9 @@ class ReverseManyRelatedObjectsDescriptor(object):
class ManyToOneRel(object):
def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None,
max_num_in_admin=None, num_extra_on_change=1, edit_inline=False,
related_name=None, limit_choices_to=None, lookup_overrides=None, raw_id_admin=False):
max_num_in_admin=None, num_extra_on_change=1, edit_inline=False,
related_name=None, limit_choices_to=None, lookup_overrides=None,
raw_id_admin=False, parent_link=False):
try:
to._meta
except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT
@ -477,6 +478,7 @@ class ManyToOneRel(object):
self.lookup_overrides = lookup_overrides or {}
self.raw_id_admin = raw_id_admin
self.multiple = True
self.parent_link = parent_link
def get_related_field(self):
"""
@ -489,14 +491,15 @@ class OneToOneRel(ManyToOneRel):
def __init__(self, to, field_name, num_in_admin=0, min_num_in_admin=None,
max_num_in_admin=None, num_extra_on_change=None, edit_inline=False,
related_name=None, limit_choices_to=None, lookup_overrides=None,
raw_id_admin=False):
raw_id_admin=False, parent_link=False):
# NOTE: *_num_in_admin and num_extra_on_change are intentionally
# ignored here. We accept them as parameters only to match the calling
# signature of ManyToOneRel.__init__().
super(OneToOneRel, self).__init__(to, field_name, num_in_admin,
edit_inline=edit_inline, related_name=related_name,
limit_choices_to=limit_choices_to,
lookup_overrides=lookup_overrides, raw_id_admin=raw_id_admin)
lookup_overrides=lookup_overrides, raw_id_admin=raw_id_admin,
parent_link=parent_link)
self.multiple = False
class ManyToManyRel(object):
@ -541,7 +544,8 @@ class ForeignKey(RelatedField, Field):
related_name=kwargs.pop('related_name', None),
limit_choices_to=kwargs.pop('limit_choices_to', None),
lookup_overrides=kwargs.pop('lookup_overrides', None),
raw_id_admin=kwargs.pop('raw_id_admin', False))
raw_id_admin=kwargs.pop('raw_id_admin', False),
parent_link=kwargs.pop('parent_link', False))
Field.__init__(self, **kwargs)
self.db_index = True

View File

@ -113,22 +113,22 @@ class Options(object):
# self.many_to_many.
if field.rel and isinstance(field.rel, ManyToManyRel):
self.local_many_to_many.insert(bisect(self.local_many_to_many, field), field)
if hasattr(self, '_m2m_cache'):
del self._m2m_cache
else:
self.local_fields.insert(bisect(self.local_fields, field), field)
if not self.pk and field.primary_key:
self.pk = field
field.serialize = False
self.setup_pk(field)
if hasattr(self, '_field_cache'):
del self._field_cache
# All of these internal caches need to be updated the next time they
# are used.
# TODO: Do this more neatly. (Also, use less caches!)
if hasattr(self, '_field_cache'):
del self._field_cache
if hasattr(self, '_m2m_cache'):
del self._m2m_cache
if hasattr(self, '_name_map'):
del self._name_map
def setup_pk(self, field):
if not self.pk and field.primary_key:
self.pk = field
field.serialize = False
def __repr__(self):
return '<Options for %s>' % self.object_name
@ -315,7 +315,7 @@ class Options(object):
parent_list = self.get_parent_list()
for parent in self.parents:
for obj, model in parent._meta.get_all_related_objects_with_model():
if obj.field.creation_counter < 0 and obj.model not in parent_list:
if (obj.field.creation_counter < 0 or obj.field.rel.parent_link) and obj.model not in parent_list:
continue
if not model:
cache[obj] = parent

View File

@ -1000,6 +1000,21 @@ As with ``ForeignKey``, a relationship to self can be defined by using the
string ``"self"`` instead of the model name; references to as-yet undefined
models can be made by using a string containing the model name.
Finally, ``OneToOneField`` takes the following extra option:
======================= ============================================================
Argument Description
======================= ============================================================
``parent_link`` When ``True`` and used in a model inherited from
another model, indicates that this field should
be used as the link from the child back to the
parent. See `Model inheritance`_ for more
details.
**New in Django development version**
======================= ============================================================
**New in Django development version:** ``OneToOneField`` classes used to
automatically become the primary key on a model. This is no longer true,
although you can manually pass in the ``primary_key`` attribute if you like.
@ -1036,6 +1051,14 @@ Model metadata is "anything that's not a field", such as ordering options, etc.
Here's a list of all possible ``Meta`` options. No options are required. Adding
``class Meta`` to a model is completely optional.
``abstract``
------------
**New in Django development version**
When set to ``True``, denotes this model as an abstract base class. See
`Abstract base classes`_ for more details. Defaults to ``False``.
``db_table``
------------
@ -2192,6 +2215,20 @@ For more information about reverse relations, refer to the `Database API
reference`_ . For now, just remember to run ``manage.py validate`` when
you're writing your models and pay attention to the error messages.
Specifying the parent link field
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
As mentioned, Django will automatically create a ``OneToOneField`` linking
your child class back any non-abstract parent models. If you want to control
the name of the attribute linking back to the parent, you can create your own
link field and pass it ``parent_link=True``. For example, to explicitly
specify the field that will link ``Supplier`` to ``Place`` in the above
example, you could write::
class Supplier(Place):
parent = models.OneToOneField(Place, parent_link=True)
...
Multiple inheritance
--------------------

View File

@ -67,6 +67,8 @@ class Supplier(Place):
return u"%s the supplier" % self.name
class ParkingLot(Place):
# An explicit link to the parent (we can control the attribute name).
parent = models.OneToOneField(Place, primary_key=True, parent_link=True)
main_site = models.ForeignKey(Place, related_name='lot')
def __unicode__(self):