From ec5ddd373003c8ae565707eb1e00fdbc501ce03e Mon Sep 17 00:00:00 2001
From: Sebastian Noack <sebastian.noack@gmail.com>
Date: Fri, 2 May 2008 13:22:20 +0200
Subject: [PATCH] Now all managers, not only the default manager gets copied to the derived model of an abstract model (#7154).

---
 django/db/models/base.py                     |   16 +++++++---------
 django/db/models/manager.py                  |   21 ++++++---------------
 django/db/models/options.py                  |    1 +
 tests/modeltests/model_inheritance/models.py |   24 +++++++++++++++++++++++-
 4 files changed, 37 insertions(+), 25 deletions(-)

diff --git a/django/db/models/base.py b/django/db/models/base.py
index 0ee2256..2f0ffc3 100644
--- a/django/db/models/base.py
+++ b/django/db/models/base.py
@@ -65,12 +65,6 @@ class ModelBase(type):
                 if not hasattr(meta, 'get_latest_by'):
                     new_class._meta.get_latest_by = base_meta.get_latest_by
 
-        old_default_mgr = None
-        if getattr(new_class, '_default_manager', None):
-            # We have a parent who set the default manager.
-            if new_class._default_manager.model._meta.abstract:
-                old_default_mgr = new_class._default_manager
-            new_class._default_manager = None
         if getattr(new_class._meta, 'app_label', None) is None:
             # Figure out the app_label by looking one level up.
             # For 'django.contrib.sites.models', this would be 'sites'.
@@ -106,7 +100,7 @@ class ModelBase(type):
                     new_class.add_to_class(attr_name, field)
                 new_class._meta.parents[base] = field
             else:
-                # The abstract base class case.
+                # Copy fields from the abstract base class.
                 names = set([f.name for f in new_class._meta.local_fields + new_class._meta.many_to_many])
                 for field in base._meta.local_fields + base._meta.local_many_to_many:
                     if field.name in names:
@@ -114,6 +108,12 @@ class ModelBase(type):
                                 % (field.name, name, base.__name__))
                     new_class.add_to_class(field.name, copy.deepcopy(field))
 
+            # Copy managers from the base model.
+            for mgr_attr, mgr in base._meta.base_managers.iteritems():
+                if mgr_attr in new_class.__dict__:
+                    continue
+                new_class.add_to_class(mgr_attr, copy.copy(mgr))
+
         if abstract:
             # Abstract base models can't be instantiated and don't appear in
             # the list of models for an app. We do the final setup for them a
@@ -122,8 +122,6 @@ class ModelBase(type):
             new_class.Meta = attr_meta
             return new_class
 
-        if old_default_mgr and not new_class._default_manager:
-            new_class._default_manager = old_default_mgr._copy_to_model(new_class)
         new_class._prepare()
         register_models(new_class._meta.app_label, new_class)
 
diff --git a/django/db/models/manager.py b/django/db/models/manager.py
index 3a9da34..dfb2dc5 100644
--- a/django/db/models/manager.py
+++ b/django/db/models/manager.py
@@ -31,21 +31,12 @@ class Manager(object):
 
     def contribute_to_class(self, model, name):
         # TODO: Use weakref because of possible memory leak / circular reference.
-        self.model = model
-        setattr(model, name, ManagerDescriptor(self))
-        if not getattr(model, '_default_manager', None) or self.creation_counter < model._default_manager.creation_counter:
-            model._default_manager = self
-
-    def _copy_to_model(self, model):
-        """
-        Makes a copy of the manager and assigns it to 'model', which should be
-        a child of the existing model (used when inheriting a manager from an
-        abstract base class).
-        """
-        assert issubclass(model, self.model)
-        mgr = copy.copy(self)
-        mgr.model = model
-        return mgr
+        if not model._meta.abstract:
+            self.model = model
+            setattr(model, name, ManagerDescriptor(self))
+            if not getattr(model, '_default_manager', None) or self.creation_counter == model._default_manager.creation_counter:
+                model._default_manager = self
+        model._meta.base_managers[name] = self
 
     #######################
     # PROXIES TO QUERYSET #
diff --git a/django/db/models/options.py b/django/db/models/options.py
index 5802ead..d1feed9 100644
--- a/django/db/models/options.py
+++ b/django/db/models/options.py
@@ -44,6 +44,7 @@ class Options(object):
         self.one_to_one_field = None
         self.abstract = False
         self.parents = SortedDict()
+        self.base_managers = SortedDict()
 
     def contribute_to_class(self, cls, name):
         cls._meta = self
diff --git a/tests/modeltests/model_inheritance/models.py b/tests/modeltests/model_inheritance/models.py
index b1a751f..0f959a7 100644
--- a/tests/modeltests/model_inheritance/models.py
+++ b/tests/modeltests/model_inheritance/models.py
@@ -18,10 +18,21 @@ from django.db import models
 # Abstract base classes
 #
 
+class AdultManager(models.Manager):
+    def get_query_set(self):
+        return super(AdultManager, self).get_query_set().filter(age__gte=18)
+
+class Adult21Manager(models.Manager):
+    def get_query_set(self):
+        return super(Adult21Manager, self).get_query_set().filter(age__gte=21)
+
 class CommonInfo(models.Model):
     name = models.CharField(max_length=50)
     age = models.PositiveIntegerField()
 
+    objects = models.Manager()
+    adults = AdultManager()
+
     class Meta:
         abstract = True
         ordering = ['name']
@@ -30,6 +41,8 @@ class CommonInfo(models.Model):
         return u'%s %s' % (self.__class__.__name__, self.name)
 
 class Worker(CommonInfo):
+    adults = Adult21Manager()
+
     job = models.CharField(max_length=50)
 
 class Student(CommonInfo):
@@ -102,7 +115,7 @@ __test__ = {'API_TESTS':"""
 
 >>> w = Worker(name='Fred', age=35, job='Quarry worker')
 >>> w.save()
->>> w2 = Worker(name='Barney', age=34, job='Quarry worker')
+>>> w2 = Worker(name='Barney', age=20, job='Quarry worker')
 >>> w2.save()
 >>> s = Student(name='Pebbles', age=5, school_class='1B')
 >>> s.save()
@@ -121,6 +134,15 @@ u'Student Pebbles'
 >>> Student._meta.ordering
 []
 
+# The children inherit the custom managers from their parents, but children can
+# still override it.
+>>> Student(name='Bill', age=18, school_class='11A').save()
+>>> Student.adults.all()
+[<Student: Student Bill>]
+
+>>> Worker.adults.all()
+[<Worker: Worker Fred>]
+
 # However, the CommonInfo class cannot be used as a normal model (it doesn't
 # exist as a model).
 >>> CommonInfo.objects.all()
-- 
1.5.3.7

