magic-removal: Added first stab at many-to-one descriptor lookup. Now poll_obj.choice_set is created. poll_obj.get_choice_list(), poll_obj.get_choice() and poll_obj.get_choice_count() are still supported temporarily, but that code was marked with a TODO to be deleted.
git-svn-id: http://code.djangoproject.com/svn/django/branches/magic-removal@2141 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
a6139af645
commit
b22ebaf466
|
@ -202,7 +202,7 @@ class Model(object):
|
|||
seen_objs.setdefault(self.__class__, {})[pk_val] = self
|
||||
|
||||
for related in self._meta.get_all_related_objects():
|
||||
rel_opts_name = related.get_accessor_name()
|
||||
rel_opts_name = related.OLD_get_accessor_name()
|
||||
if isinstance(related.field.rel, OneToOne):
|
||||
try:
|
||||
sub_obj = getattr(self, 'get_%s' % rel_opts_name)()
|
||||
|
|
|
@ -6,6 +6,7 @@ from django.utils.functional import curry
|
|||
from django.core import validators
|
||||
from django import forms
|
||||
from django.dispatch import dispatcher
|
||||
import types
|
||||
|
||||
# Values for Relation.edit_inline.
|
||||
TABULAR, STACKED = 1, 2
|
||||
|
@ -61,6 +62,38 @@ class RelatedField(object):
|
|||
related = RelatedObject(other, cls, self)
|
||||
self.contribute_to_related_class(other, related)
|
||||
|
||||
class RelatedObjectDescriptor(object):
|
||||
# This class provides the functionality that makes the related-object
|
||||
# managers available as attributes on a model class.
|
||||
# In the example "poll.choice_set", the choice_set attribute is a
|
||||
# RelatedObjectDescriptor instance.
|
||||
def __init__(self, related):
|
||||
self.related = related # RelatedObject instance
|
||||
self.manager = None
|
||||
|
||||
def __get__(self, instance, instance_type=None):
|
||||
if instance is None:
|
||||
raise AttributeError, "Manager must be accessed via instance"
|
||||
else:
|
||||
if not self.manager:
|
||||
# Dynamically create a class that subclasses the related
|
||||
# model's default manager.
|
||||
self.manager = types.ClassType('RelatedManager', (self.related.model._default_manager.__class__,), {})()
|
||||
|
||||
# Set core_filters on the new manager to limit it to the
|
||||
# foreign-key relationship.
|
||||
rel_field = self.related.field
|
||||
self.manager.core_filters = {'%s__%s__exact' % (rel_field.name, rel_field.rel.to._meta.pk.name): getattr(instance, rel_field.rel.get_related_field().attname)}
|
||||
|
||||
# Prepare the manager.
|
||||
# TODO: We need to set self.manager.klass because
|
||||
# self.manager._prepare() expects that self.manager.klass is
|
||||
# set. This is slightly hackish.
|
||||
self.manager.klass = self.related.model
|
||||
self.manager._prepare()
|
||||
|
||||
return self.manager
|
||||
|
||||
class ForeignKey(RelatedField, Field):
|
||||
empty_strings_allowed = False
|
||||
def __init__(self, to, to_field=None, **kwargs):
|
||||
|
@ -151,7 +184,13 @@ class ForeignKey(RelatedField, Field):
|
|||
setattr(cls, 'get_%s' % self.name, curry(cls._get_foreign_key_object, field_with_rel=self))
|
||||
|
||||
def contribute_to_related_class(self, cls, related):
|
||||
rel_obj_name = related.get_accessor_name()
|
||||
setattr(cls, related.get_accessor_name(), RelatedObjectDescriptor(related))
|
||||
|
||||
# TODO: Delete the rest of this function and RelatedObject.OLD_get_accessor_name()
|
||||
# to remove support for old-style related lookup.
|
||||
|
||||
rel_obj_name = related.OLD_get_accessor_name
|
||||
|
||||
# Add "get_thingie" methods for many-to-one related objects.
|
||||
# EXAMPLE: Poll.get_choice()
|
||||
setattr(cls, 'get_%s' % rel_obj_name, curry(cls._get_related, method_name='get_object', rel_class=related.model, rel_field=related.field))
|
||||
|
@ -206,7 +245,7 @@ class OneToOneField(RelatedField, IntegerField):
|
|||
setattr(cls, 'get_%s' % self.name, curry(cls._get_foreign_key_object, field_with_rel=self))
|
||||
|
||||
def contribute_to_related_class(self, cls, related):
|
||||
rel_obj_name = related.get_accessor_name()
|
||||
rel_obj_name = related.OLD_get_accessor_name()
|
||||
# Add "get_thingie" methods for one-to-one related objects.
|
||||
# EXAMPLE: Place.get_restaurants_restaurant()
|
||||
setattr(cls, 'get_%s' % rel_obj_name,
|
||||
|
@ -296,7 +335,7 @@ class ManyToManyField(RelatedField, Field):
|
|||
setattr(cls, 'set_%s' % self.name, curry(cls._set_many_to_many_objects, field_with_rel=self))
|
||||
|
||||
def contribute_to_related_class(self, cls, related):
|
||||
rel_obj_name = related.get_accessor_name()
|
||||
rel_obj_name = related.OLD_get_accessor_name()
|
||||
setattr(cls, 'get_%s' % rel_obj_name, curry(cls._get_related_many_to_many, method_name='get_object', rel_class=related.model, rel_field=related.field))
|
||||
setattr(cls, 'get_%s_count' % rel_obj_name, curry(cls._get_related_many_to_many, method_name='get_count', rel_class=related.model, rel_field=related.field))
|
||||
setattr(cls, 'get_%s_list' % rel_obj_name, curry(cls._get_related_many_to_many, method_name='get_list', rel_class=related.model, rel_field=related.field))
|
||||
|
|
|
@ -154,7 +154,7 @@ class AutomaticManipulator(forms.Manipulator):
|
|||
if self.change:
|
||||
if rel_new_data[related.opts.pk.name][0]:
|
||||
try:
|
||||
old_rel_obj = getattr(self.original_object, 'get_%s' % related.get_accessor_name() )(**{'%s__exact' % related.opts.pk.name: rel_new_data[related.opts.pk.attname][0]})
|
||||
old_rel_obj = getattr(self.original_object, 'get_%s' % related.OLD_get_accessor_name() )(**{'%s__exact' % related.opts.pk.name: rel_new_data[related.opts.pk.attname][0]})
|
||||
except ObjectDoesNotExist:
|
||||
pass
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ class RelatedObject(object):
|
|||
def get_list(self, parent_instance=None):
|
||||
"Get the list of this type of object from an instance of the parent class."
|
||||
if parent_instance != None:
|
||||
func_name = 'get_%s_list' % self.get_accessor_name()
|
||||
func_name = 'get_%s_list' % self.OLD_get_accessor_name()
|
||||
func = getattr(parent_instance, func_name)
|
||||
return func()
|
||||
else:
|
||||
|
@ -80,19 +80,12 @@ class RelatedObject(object):
|
|||
def get_accessor_name(self):
|
||||
# This method encapsulates the logic that decides what name to give an
|
||||
# accessor descriptor that retrieves related many-to-one or
|
||||
# many-to-many objects. Usually it just uses the lower-cased
|
||||
# object_name, but if the related object is in another app, the related
|
||||
# object's app_label is appended.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# # Normal case -- a related object in the same app.
|
||||
# # This method returns "choice".
|
||||
# Poll.choice_set
|
||||
#
|
||||
# # A related object in a different app.
|
||||
# # This method returns "lcom_bestofaward".
|
||||
# Place.lcom_bestofaward_set # "lcom_bestofaward"
|
||||
# many-to-many objects. It uses the lower-cased object_name + "_set",
|
||||
# but this can be overridden with the "related_name" option.
|
||||
return self.field.rel.related_name or (self.opts.object_name.lower() + '_set')
|
||||
|
||||
# TODO: Remove this.
|
||||
def OLD_get_accessor_name(self):
|
||||
rel_obj_name = self.field.rel.related_name or self.opts.object_name.lower()
|
||||
if self.parent_model._meta.app_label != self.opts.app_label:
|
||||
rel_obj_name = '%s_%s' % (self.opts.app_label, rel_obj_name)
|
||||
|
|
Loading…
Reference in New Issue