diff --git a/django/db/models/manager.py b/django/db/models/manager.py index 757aa81c15ec8..1338c49c85aac 100644 --- a/django/db/models/manager.py +++ b/django/db/models/manager.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): diff --git a/django/db/models/query.py b/django/db/models/query.py index 9ade0f6059d14..1d1f202cd728e 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -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() ######################## diff --git a/tests/custom_managers/models.py b/tests/custom_managers/models.py index 08c897ca41ac9..ccf634c6638b5 100644 --- a/tests/custom_managers/models.py +++ b/tests/custom_managers/models.py @@ -35,6 +35,16 @@ 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) @@ -42,6 +52,7 @@ class Person(models.Model): 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) diff --git a/tests/custom_managers/tests.py b/tests/custom_managers/tests.py index 725ff9c4352dd..f534f6a82a00b 100644 --- a/tests/custom_managers/tests.py +++ b/tests/custom_managers/tests.py @@ -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)