Seperate managers from their descriptors.

git-svn-id: http://code.djangoproject.com/svn/django/branches/magic-removal@1697 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Robert Wittams 2005-12-16 15:14:12 +00:00
parent 6f0861b256
commit 7aeeae67ee
2 changed files with 35 additions and 25 deletions

View File

@ -2,7 +2,7 @@ from django.db.models.manipulators import ManipulatorDescriptor, ModelAddManipul
from django.db.models.fields import Field, DateField, FileField, ImageField, AutoField from django.db.models.fields import Field, DateField, FileField, ImageField, AutoField
from django.db.models.fields import OneToOne, ManyToOne, ManyToMany, RECURSIVE_RELATIONSHIP_CONSTANT from django.db.models.fields import OneToOne, ManyToOne, ManyToMany, RECURSIVE_RELATIONSHIP_CONSTANT
from django.db.models.related import RelatedObject from django.db.models.related import RelatedObject
from django.db.models.manager import Manager from django.db.models.manager import Manager, ManagerDescriptor
from django.db.models.query import orderlist2sql from django.db.models.query import orderlist2sql
from django.db.models.options import Options from django.db.models.options import Options
from django.db import connection, backend from django.db import connection, backend
@ -106,20 +106,14 @@ class ModelBase(type):
opts.db_table = "%s_%s" % (app_label, opts.module_name) opts.db_table = "%s_%s" % (app_label, opts.module_name)
new_class._meta = opts new_class._meta = opts
# Create the default manager, if needed. for m_name, m in managers:
# TODO: Use weakref because of possible memory leak / circular reference. new_class.add_to_class(m_name, m)
if managers:
for m_name, m in managers: if not hasattr(new_class, '_default_manager'):
m._prepare(new_class) # Create the default manager, if needed.
setattr(new_class, m_name, m)
new_class._default_manager = managers[0][1]
else:
if hasattr(new_class, 'objects'): if hasattr(new_class, 'objects'):
raise ValueError, "Model %s must specify a custom Manager, because it has a field named 'objects'" % name raise ValueError, "Model %s must specify a custom Manager, because it has a field named 'objects'" % name
m = Manager() new_class.add_to_class('objects', Manager())
m._prepare(new_class)
new_class.objects = m
new_class._default_manager = m
new_class._prepare() new_class._prepare()
@ -139,6 +133,13 @@ class ModelBase(type):
class Model(object): class Model(object):
__metaclass__ = ModelBase __metaclass__ = ModelBase
def add_to_class(cls, name, attribute):
if hasattr(attribute, 'contribute_to_class'):
attribute.contribute_to_class(cls, name)
else:
setattr(cls, name, attribute)
add_to_class = classmethod(add_to_class)
AddManipulator = ManipulatorDescriptor('AddManipulator', ModelAddManipulator) AddManipulator = ManipulatorDescriptor('AddManipulator', ModelAddManipulator)
ChangeManipulator = ManipulatorDescriptor('ChangeManipulator', ModelChangeManipulator) ChangeManipulator = ManipulatorDescriptor('ChangeManipulator', ModelChangeManipulator)

View File

@ -8,25 +8,17 @@ from django.db.models.query import handle_legacy_orderlist, orderlist2sql, order
# Larger values are slightly faster at the expense of more storage space. # Larger values are slightly faster at the expense of more storage space.
GET_ITERATOR_CHUNK_SIZE = 100 GET_ITERATOR_CHUNK_SIZE = 100
class Manager(object): class Manager(object):
# Tracks each time a Field instance is created. Used to retain order.
# Tracks each time a Field instance is created. Used to retain order.
creation_counter = 0 creation_counter = 0
def __init__(self): def __init__(self):
# Increase the creation counter, and save our local copy. # Increase the creation counter, and save our local copy.
self.creation_counter = Manager.creation_counter self.creation_counter = Manager.creation_counter
Manager.creation_counter += 1 Manager.creation_counter += 1
self.klass = None
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, klass): def _prepare(self, klass):
# Creates some methods once self.klass._meta has been populated.
self.klass = klass self.klass = klass
if self.klass._meta.get_latest_by: if self.klass._meta.get_latest_by:
self.get_latest = self.__get_latest self.get_latest = self.__get_latest
@ -34,6 +26,14 @@ class Manager(object):
if isinstance(f, DateField): if isinstance(f, DateField):
setattr(self, 'get_%s_list' % f.name, curry(self.__get_date_list, f)) setattr(self, 'get_%s_list' % f.name, curry(self.__get_date_list, f))
def contribute_to_class(self, klass, name):
# TODO: Use weakref because of possible memory leak / circular reference.
self._prepare(klass)
setattr(klass,name, ManagerDescriptor(self))
if not hasattr(klass, '_default_manager') or \
self.creation_counter < klass._default_manager.creation_counter:
klass._default_manager = self
def _get_sql_clause(self, **kwargs): def _get_sql_clause(self, **kwargs):
def quote_only_if_word(word): def quote_only_if_word(word):
if ' ' in word: if ' ' in word:
@ -204,4 +204,13 @@ class Manager(object):
# We have to manually run typecast_timestamp(str()) on the results, because # We have to manually run typecast_timestamp(str()) on the results, because
# MySQL doesn't automatically cast the result of date functions as datetime # MySQL doesn't automatically cast the result of date functions as datetime
# objects -- MySQL returns the values as strings, instead. # objects -- MySQL returns the values as strings, instead.
return [typecast_timestamp(str(row[0])) for row in cursor.fetchall()] return [typecast_timestamp(str(row[0])) for row in cursor.fetchall()]
class ManagerDescriptor(object):
def __init__(self, manager):
self.manager = manager
def __get__(self, instance, type=None):
if instance != None:
raise AttributeError, "Manager isn't accessible via %s instances" % type.__name__
return self.manager