mirror of
https://github.com/django/django.git
synced 2025-07-06 18:59:13 +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'):
|
if not hasattr(meta, 'get_latest_by'):
|
||||||
new_class._meta.get_latest_by = base_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:
|
if getattr(new_class, '_default_manager', None) is not None:
|
||||||
# We have a parent who set the default manager. We need to override
|
# We have a parent who set the default manager. We need to override
|
||||||
# this.
|
# this.
|
||||||
@ -93,13 +78,32 @@ class ModelBase(type):
|
|||||||
for obj_name, obj in attrs.items():
|
for obj_name, obj in attrs.items():
|
||||||
new_class.add_to_class(obj_name, obj)
|
new_class.add_to_class(obj_name, obj)
|
||||||
|
|
||||||
for parent in abstract_parents:
|
# Do the appropriate setup for any model parents.
|
||||||
names = [f.name for f in new_class._meta.local_fields + new_class._meta.many_to_many]
|
o2o_map = dict([(f.rel.to, f) for f in new_class._meta.local_fields
|
||||||
for field in parent._meta.local_fields:
|
if isinstance(f, OneToOneField)])
|
||||||
if field.name in names:
|
for base in parents:
|
||||||
raise TypeError('Local field %r in class %r clashes with field of similar name from abstract base class %r'
|
if not hasattr(base, '_meta'):
|
||||||
% (field.name, name, parent.__name__))
|
# Things without _meta aren't functional models, so they're
|
||||||
new_class.add_to_class(field.name, field)
|
# 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:
|
if abstract:
|
||||||
# Abstract base models can't be instantiated and don't appear in
|
# Abstract base models can't be instantiated and don't appear in
|
||||||
|
@ -461,8 +461,9 @@ class ReverseManyRelatedObjectsDescriptor(object):
|
|||||||
|
|
||||||
class ManyToOneRel(object):
|
class ManyToOneRel(object):
|
||||||
def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None,
|
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,
|
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):
|
related_name=None, limit_choices_to=None, lookup_overrides=None,
|
||||||
|
raw_id_admin=False, parent_link=False):
|
||||||
try:
|
try:
|
||||||
to._meta
|
to._meta
|
||||||
except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT
|
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.lookup_overrides = lookup_overrides or {}
|
||||||
self.raw_id_admin = raw_id_admin
|
self.raw_id_admin = raw_id_admin
|
||||||
self.multiple = True
|
self.multiple = True
|
||||||
|
self.parent_link = parent_link
|
||||||
|
|
||||||
def get_related_field(self):
|
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,
|
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,
|
max_num_in_admin=None, num_extra_on_change=None, edit_inline=False,
|
||||||
related_name=None, limit_choices_to=None, lookup_overrides=None,
|
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
|
# NOTE: *_num_in_admin and num_extra_on_change are intentionally
|
||||||
# ignored here. We accept them as parameters only to match the calling
|
# ignored here. We accept them as parameters only to match the calling
|
||||||
# signature of ManyToOneRel.__init__().
|
# signature of ManyToOneRel.__init__().
|
||||||
super(OneToOneRel, self).__init__(to, field_name, num_in_admin,
|
super(OneToOneRel, self).__init__(to, field_name, num_in_admin,
|
||||||
edit_inline=edit_inline, related_name=related_name,
|
edit_inline=edit_inline, related_name=related_name,
|
||||||
limit_choices_to=limit_choices_to,
|
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
|
self.multiple = False
|
||||||
|
|
||||||
class ManyToManyRel(object):
|
class ManyToManyRel(object):
|
||||||
@ -541,7 +544,8 @@ class ForeignKey(RelatedField, Field):
|
|||||||
related_name=kwargs.pop('related_name', None),
|
related_name=kwargs.pop('related_name', None),
|
||||||
limit_choices_to=kwargs.pop('limit_choices_to', None),
|
limit_choices_to=kwargs.pop('limit_choices_to', None),
|
||||||
lookup_overrides=kwargs.pop('lookup_overrides', 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)
|
Field.__init__(self, **kwargs)
|
||||||
|
|
||||||
self.db_index = True
|
self.db_index = True
|
||||||
|
@ -113,22 +113,22 @@ class Options(object):
|
|||||||
# self.many_to_many.
|
# self.many_to_many.
|
||||||
if field.rel and isinstance(field.rel, ManyToManyRel):
|
if field.rel and isinstance(field.rel, ManyToManyRel):
|
||||||
self.local_many_to_many.insert(bisect(self.local_many_to_many, field), field)
|
self.local_many_to_many.insert(bisect(self.local_many_to_many, field), field)
|
||||||
|
if hasattr(self, '_m2m_cache'):
|
||||||
|
del self._m2m_cache
|
||||||
else:
|
else:
|
||||||
self.local_fields.insert(bisect(self.local_fields, field), field)
|
self.local_fields.insert(bisect(self.local_fields, field), field)
|
||||||
if not self.pk and field.primary_key:
|
self.setup_pk(field)
|
||||||
self.pk = field
|
if hasattr(self, '_field_cache'):
|
||||||
field.serialize = False
|
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'):
|
if hasattr(self, '_name_map'):
|
||||||
del 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):
|
def __repr__(self):
|
||||||
return '<Options for %s>' % self.object_name
|
return '<Options for %s>' % self.object_name
|
||||||
|
|
||||||
@ -315,7 +315,7 @@ class Options(object):
|
|||||||
parent_list = self.get_parent_list()
|
parent_list = self.get_parent_list()
|
||||||
for parent in self.parents:
|
for parent in self.parents:
|
||||||
for obj, model in parent._meta.get_all_related_objects_with_model():
|
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
|
continue
|
||||||
if not model:
|
if not model:
|
||||||
cache[obj] = parent
|
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
|
string ``"self"`` instead of the model name; references to as-yet undefined
|
||||||
models can be made by using a string containing the model name.
|
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
|
**New in Django development version:** ``OneToOneField`` classes used to
|
||||||
automatically become the primary key on a model. This is no longer true,
|
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.
|
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
|
Here's a list of all possible ``Meta`` options. No options are required. Adding
|
||||||
``class Meta`` to a model is completely optional.
|
``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``
|
``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
|
reference`_ . For now, just remember to run ``manage.py validate`` when
|
||||||
you're writing your models and pay attention to the error messages.
|
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
|
Multiple inheritance
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
|
@ -67,6 +67,8 @@ class Supplier(Place):
|
|||||||
return u"%s the supplier" % self.name
|
return u"%s the supplier" % self.name
|
||||||
|
|
||||||
class ParkingLot(Place):
|
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')
|
main_site = models.ForeignKey(Place, related_name='lot')
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user