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:
parent
0c20e88e65
commit
b7be3d63e3
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
--------------------
|
||||
|
||||
|
@ -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):
|
||||
|
Loading…
x
Reference in New Issue
Block a user