1
0
mirror of https://github.com/django/django.git synced 2025-06-05 03:29:12 +00:00

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:
Adrian Holovaty 2006-01-28 00:12:26 +00:00
parent a6139af645
commit b22ebaf466
4 changed files with 51 additions and 19 deletions

View File

@ -202,7 +202,7 @@ class Model(object):
seen_objs.setdefault(self.__class__, {})[pk_val] = self seen_objs.setdefault(self.__class__, {})[pk_val] = self
for related in self._meta.get_all_related_objects(): 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): if isinstance(related.field.rel, OneToOne):
try: try:
sub_obj = getattr(self, 'get_%s' % rel_opts_name)() sub_obj = getattr(self, 'get_%s' % rel_opts_name)()

View File

@ -6,6 +6,7 @@ from django.utils.functional import curry
from django.core import validators from django.core import validators
from django import forms from django import forms
from django.dispatch import dispatcher from django.dispatch import dispatcher
import types
# Values for Relation.edit_inline. # Values for Relation.edit_inline.
TABULAR, STACKED = 1, 2 TABULAR, STACKED = 1, 2
@ -61,6 +62,38 @@ class RelatedField(object):
related = RelatedObject(other, cls, self) related = RelatedObject(other, cls, self)
self.contribute_to_related_class(other, related) 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): class ForeignKey(RelatedField, Field):
empty_strings_allowed = False empty_strings_allowed = False
def __init__(self, to, to_field=None, **kwargs): 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)) setattr(cls, 'get_%s' % self.name, curry(cls._get_foreign_key_object, field_with_rel=self))
def contribute_to_related_class(self, cls, related): 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. # Add "get_thingie" methods for many-to-one related objects.
# EXAMPLE: Poll.get_choice() # 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)) 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)) setattr(cls, 'get_%s' % self.name, curry(cls._get_foreign_key_object, field_with_rel=self))
def contribute_to_related_class(self, cls, related): 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. # Add "get_thingie" methods for one-to-one related objects.
# EXAMPLE: Place.get_restaurants_restaurant() # EXAMPLE: Place.get_restaurants_restaurant()
setattr(cls, 'get_%s' % rel_obj_name, 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)) 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): 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' % 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_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)) 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))

View File

@ -154,7 +154,7 @@ class AutomaticManipulator(forms.Manipulator):
if self.change: if self.change:
if rel_new_data[related.opts.pk.name][0]: if rel_new_data[related.opts.pk.name][0]:
try: 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: except ObjectDoesNotExist:
pass pass

View File

@ -44,7 +44,7 @@ class RelatedObject(object):
def get_list(self, parent_instance=None): def get_list(self, parent_instance=None):
"Get the list of this type of object from an instance of the parent class." "Get the list of this type of object from an instance of the parent class."
if parent_instance != None: 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) func = getattr(parent_instance, func_name)
return func() return func()
else: else:
@ -80,19 +80,12 @@ class RelatedObject(object):
def get_accessor_name(self): def get_accessor_name(self):
# This method encapsulates the logic that decides what name to give an # This method encapsulates the logic that decides what name to give an
# accessor descriptor that retrieves related many-to-one or # accessor descriptor that retrieves related many-to-one or
# many-to-many objects. Usually it just uses the lower-cased # many-to-many objects. It uses the lower-cased object_name + "_set",
# object_name, but if the related object is in another app, the related # but this can be overridden with the "related_name" option.
# object's app_label is appended. return self.field.rel.related_name or (self.opts.object_name.lower() + '_set')
#
# Examples: # TODO: Remove this.
# def OLD_get_accessor_name(self):
# # 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"
rel_obj_name = self.field.rel.related_name or self.opts.object_name.lower() 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: if self.parent_model._meta.app_label != self.opts.app_label:
rel_obj_name = '%s_%s' % (self.opts.app_label, rel_obj_name) rel_obj_name = '%s_%s' % (self.opts.app_label, rel_obj_name)