Permalink
Browse files

POC to address concerns about manager-only methods. Refs https://code…

  • Loading branch information...
loic committed Jul 3, 2013
1 parent ed72280 commit 2e129ae8aba862054373a3c5789493bc456d1ed7
Showing with 30 additions and 6 deletions.
  1. +3 −3 django/db/models/manager.py
  2. +8 −3 django/db/models/query.py
  3. +11 −0 tests/custom_managers/models.py
  4. +8 −0 tests/custom_managers/tests.py
@@ -58,7 +58,7 @@ class RenameManagerMethods(RenameMethodsBase):
class _Manager(six.with_metaclass(RenameManagerMethods)):
# Tracks each time a Manager instance is created. Used to retain order.
creation_counter = 0
- queryset_class = QuerySet
+ _queryset_class = QuerySet
def __init__(self):
super(_Manager, self).__init__()
@@ -121,7 +121,7 @@ def get_queryset(self):
"""Returns a new QuerySet object. Subclasses can override this method
to easily customize the behavior of the Manager.
"""
- return self.queryset_class(self.model, using=self._db)
+ return self._queryset_class(self.model, using=self._db)
def all(self):
# All can't be proxied to QuerySet, as prefetch_related is lost on
@@ -134,7 +134,7 @@ def _insert(self, objs, fields, **kwargs):
def raw(self, raw_query, params=None, *args, **kwargs):
return RawQuerySet(raw_query=raw_query, model=self.model, params=params, using=self._db, *args, **kwargs)
-Manager = QuerySet.manager_cls(base_cls=_Manager)
+Manager = QuerySet.get_manager_class(base_cls=_Manager)
class ManagerDescriptor(object):
View
@@ -30,6 +30,9 @@ class QuerySet(object):
"""
Represents a lazy database lookup for a set of objects.
"""
+
+ base_manager_class = None
+
def __init__(self, model=None, query=None, using=None):
self.model = model
self._db = using
@@ -42,7 +45,7 @@ def __init__(self, model=None, query=None, using=None):
self._known_related_objects = {} # {rel_field, {pk: rel_obj}}
@classmethod
- def manager_cls(cls, base_cls=None):
+ def get_manager_class(cls, base_cls=None):
"""
Creates a manager class for this QuerySet class.
"""
@@ -56,18 +59,20 @@ def manager_copy(self, *args, **kwargs):
do_copy = getattr(maybe_copy, 'manager', None)
if do_copy or do_copy is None and not name.startswith('_'):
new_methods[name] = create_method(name)
+ if base_cls is None:
+ base_cls = cls.base_manager_class
if base_cls is None:
from django.db.models.manager import Manager as base_cls
manager_cls = type(
cls.__name__ + 'Manager',
(base_cls,),
new_methods)
- manager_cls.queryset_class = cls
+ manager_cls._queryset_class = cls
return manager_cls
@classmethod
def as_manager(cls, base_cls=None):
- manager_cls = cls.manager_cls(base_cls=base_cls)
+ manager_cls = cls.get_manager_class(base_cls)
return manager_cls()
########################
@@ -35,13 +35,24 @@ def bar(self, *args, **kwargs):
return self.all()
bar.manager = True
+class MyManager(models.Manager):
+ def manager_only(self):
+ pass
+
+class MyQuerySet(models.QuerySet):
+ base_manager_class = MyManager
+
+ def manager_and_queryset_method(self):
+ pass
+
@python_2_unicode_compatible
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
fun = models.BooleanField()
objects = PersonManager()
other_objects = CustomQuerySet.as_manager(base_cls=PersonManager)
+ other_other_objects = MyQuerySet.as_manager()
def __str__(self):
return "%s %s" % (self.first_name, self.last_name)
@@ -25,6 +25,14 @@ def test_manager(self):
)
print Person.other_objects.filter(fun=False)
print Person.other_objects.bar()
+
+ Person.other_other_objects.manager_and_queryset_method()
+ Person.other_other_objects.all().manager_and_queryset_method()
+
+ Person.other_other_objects.manager_only()
+ with self.assertRaises(AttributeError):
+ Person.other_other_objects.all().manager_only()
+
# The RelatedManager used on the 'books' descriptor extends the default
# manager
self.assertIsInstance(p2.books, PublishedBookManager)

0 comments on commit 2e129ae

Please sign in to comment.