magic-removal: Implemented hook for custom managers

git-svn-id: http://code.djangoproject.com/svn/django/branches/magic-removal@1647 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Adrian Holovaty 2005-12-14 18:49:48 +00:00
parent af96c19be2
commit e18d87f84e
3 changed files with 45 additions and 20 deletions

View File

@ -543,16 +543,23 @@ get_module_name = lambda class_name: class_name.lower() + 's'
get_verbose_name = lambda class_name: re.sub('([A-Z])', ' \\1', class_name).lower().strip()
class Manager(object):
def __init__(self, model_class):
self.klass = model_class
# Tracks each time a Field instance is created. Used to retain order.
creation_counter = 0
def __init__(self):
# Increase the creation counter, and save our local copy.
self.creation_counter = Manager.creation_counter
Manager.creation_counter += 1
def __get__(self, instance, type=None):
if instance != None:
raise AttributeError, "Manager isn't accessible via %s instances" % self.klass.__name__
return self
def _prepare(self):
def _prepare(self, klass):
# Creates some methods once self.klass._meta has been populated.
self.klass = klass
if self.klass._meta.get_latest_by:
self.get_latest = self.__get_latest
for f in self.klass._meta.fields:
@ -735,7 +742,7 @@ class ModelBase(type):
"Metaclass for all models"
def __new__(cls, name, bases, attrs):
# If this isn't a subclass of Model, don't do anything special.
if not bases:
if not bases or bases == (object,):
return type.__new__(cls, name, bases, attrs)
try:
@ -745,18 +752,22 @@ class ModelBase(type):
except KeyError:
meta_attrs = {}
# Gather all attributes that are Field instances.
fields = []
# Gather all attributes that are Field or Manager instances.
fields, managers = [], []
for obj_name, obj in attrs.items():
if isinstance(obj, Field):
obj.set_name(obj_name)
fields.append(obj)
del attrs[obj_name]
elif isinstance(obj, Manager):
managers.append((obj_name, obj))
del attrs[obj_name]
# Sort the fields in the order that they were created. The
# Sort the fields and managers in the order that they were created. The
# "creation_counter" is needed because metaclasses don't preserve the
# attribute order.
fields.sort(lambda x, y: x.creation_counter - y.creation_counter)
managers.sort(lambda x, y: x[1].creation_counter - y[1].creation_counter)
opts = Options(
module_name = meta_attrs.pop('module_name', get_module_name(name)),
@ -788,11 +799,6 @@ class ModelBase(type):
# Create the class, because we need it to use in currying.
new_class = type.__new__(cls, name, bases, attrs)
# Create the manager.
# TODO: Use weakref because of possible memory leak / circular reference.
# TODO: Allow for overriding of the name, and custom manager subclasses.
new_class.objects = Manager(new_class)
# Give the class a docstring -- its definition.
if new_class.__doc__ is None:
new_class.__doc__ = "%s.%s(%s)" % (opts.module_name, name, ", ".join([f.name for f in opts.fields]))
@ -816,8 +822,22 @@ class ModelBase(type):
opts.db_table = "%s_%s" % (app_label, opts.module_name)
new_class._meta = opts
# Create the default manager, if needed.
# TODO: Use weakref because of possible memory leak / circular reference.
if managers:
for m_name, m in managers:
m._prepare(new_class)
setattr(new_class, m_name, m)
new_class._default_manager = managers[0][1]
else:
if hasattr(new_class, 'objects'):
raise ValueError, "Model %s must specify a custom Manager, because it has a field named 'objects'" % name
m = Manager()
m._prepare(new_class)
new_class.objects = m
new_class._default_manager = m
new_class._prepare()
new_class.objects._prepare()
return new_class
@ -1037,7 +1057,7 @@ class Model(object):
file_name = getattr(self, 'get_%s_filename' % f.name)()
# If the file exists and no other object of this type references it,
# delete it from the filesystem.
if os.path.exists(file_name) and not self.objects.get_list(**{'%s__exact' % f.name: getattr(self, f.name)}):
if os.path.exists(file_name) and not self._default_manager.get_list(**{'%s__exact' % f.name: getattr(self, f.name)}):
os.remove(file_name)
# Run any post-delete hooks.
@ -1059,14 +1079,14 @@ class Model(object):
kwargs.setdefault('params', []).extend([param, param, getattr(self, self._meta.pk.attname)])
kwargs['order_by'] = [(not is_next and '-' or '') + field.name, (not is_next and '-' or '') + self._meta.pk.name]
kwargs['limit'] = 1
return self.objects.get_object(**kwargs)
return self._default_manager.get_object(**kwargs)
def __get_next_or_previous_in_order(self, is_next):
cachename = "__%s_order_cache" % is_next
if not hasattr(self, cachename):
op = is_next and '>' or '<'
order_field = self.order_with_respect_to
obj = self.objects.get_object(order_by=('_order',),
obj = self._default_manager.get_object(order_by=('_order',),
where=['%s %s (SELECT %s FROM %s WHERE %s=%%s)' % \
(backend.quote_name('_order'), op, backend.quote_name('_order'),
backend.quote_name(opts.db_table), backend.quote_name(opts.pk.column)),
@ -1153,7 +1173,7 @@ class Model(object):
params = {'%s__%s__exact' % (field_with_rel.rel.field_name, other_field.rel.field_name): val}
else:
params = {'%s__exact' % field_with_rel.rel.field_name: val}
retrieved_obj = field_with_rel.rel.to.objects.get_object(**params)
retrieved_obj = field_with_rel.rel.to._default_manager.get_object(**params)
setattr(self, cache_var, retrieved_obj)
return getattr(self, cache_var)
@ -1213,7 +1233,7 @@ class Model(object):
def __get_related(self, method_name, rel_class, rel_field, **kwargs):
kwargs['%s__%s__exact' % (rel_field.name, rel_field.rel.to._meta.pk.name)] = getattr(self, rel_field.rel.get_related_field().attname)
kwargs.update(rel_field.rel.lookup_overrides)
return getattr(rel_class.objects, method_name)(**kwargs)
return getattr(rel_class._default_manager, method_name)(**kwargs)
def __add_related(self, rel_class, rel_field, *args, **kwargs):
init_kwargs = dict(zip([f.attname for f in rel_class._meta.fields if f != rel_field and not isinstance(f, AutoField)], args))
@ -1238,7 +1258,7 @@ class Model(object):
# Examples: Album.get_song(), Album.get_song_list(), Album.get_song_count()
def method_get_related_many_to_many(method_name, opts, rel_mod, rel_field, self, **kwargs):
kwargs['%s__%s__exact' % (rel_field.name, opts.pk.name)] = getattr(self, opts.pk.attname)
return getattr(rel_mod.Klass.objects, method_name)(**kwargs)
return getattr(rel_mod.Klass._default_manager, method_name)(**kwargs)
# Handles setting many-to-many related objects.
# Example: Album.set_songs()

View File

@ -313,7 +313,7 @@ class Field(object):
return first_choice + list(self.choices)
rel_model = self.rel.to
return first_choice + [(getattr(x, rel_model._meta.pk.attname), str(x))
for x in rel_model.objects.get_list(**self.rel._meta.limit_choices_to)]
for x in rel_model._default_manager.get_list(**self.rel._meta.limit_choices_to)]
def get_choices_default(self):
if(self.radio_admin):

View File

@ -83,4 +83,9 @@ AttributeError: type object 'Book' has no attribute 'objects'
[Corvette, Neon]
>>> Car.fast_cars.get_list()
[Corvette]
# Each model class gets a "_default_manager" attribute, which is a reference
# to the first manager defined in the class. In this case, it's "cars".
>>> Car._default_manager.get_list(order_by=('name',))
[Corvette, Neon]
"""