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
|
b
|
class ModelBase(type):
|
65 | 65 | if not hasattr(meta, 'get_latest_by'): |
66 | 66 | new_class._meta.get_latest_by = base_meta.get_latest_by |
67 | 67 | |
68 | | old_default_mgr = None |
69 | | if getattr(new_class, '_default_manager', None): |
70 | | # We have a parent who set the default manager. |
71 | | if new_class._default_manager.model._meta.abstract: |
72 | | old_default_mgr = new_class._default_manager |
73 | | new_class._default_manager = None |
74 | 68 | if getattr(new_class._meta, 'app_label', None) is None: |
75 | 69 | # Figure out the app_label by looking one level up. |
76 | 70 | # For 'django.contrib.sites.models', this would be 'sites'. |
… |
… |
class ModelBase(type):
|
106 | 100 | new_class.add_to_class(attr_name, field) |
107 | 101 | new_class._meta.parents[base] = field |
108 | 102 | else: |
109 | | # The abstract base class case. |
| 103 | # Copy fields from the abstract base class. |
110 | 104 | names = set([f.name for f in new_class._meta.local_fields + new_class._meta.many_to_many]) |
111 | 105 | for field in base._meta.local_fields + base._meta.local_many_to_many: |
112 | 106 | if field.name in names: |
… |
… |
class ModelBase(type):
|
114 | 108 | % (field.name, name, base.__name__)) |
115 | 109 | new_class.add_to_class(field.name, copy.deepcopy(field)) |
116 | 110 | |
| 111 | # Copy managers from the base model. |
| 112 | for mgr_attr, mgr in base._meta.base_managers.iteritems(): |
| 113 | if mgr_attr in new_class.__dict__: |
| 114 | continue |
| 115 | new_class.add_to_class(mgr_attr, copy.copy(mgr)) |
| 116 | |
117 | 117 | if abstract: |
118 | 118 | # Abstract base models can't be instantiated and don't appear in |
119 | 119 | # the list of models for an app. We do the final setup for them a |
… |
… |
class ModelBase(type):
|
122 | 122 | new_class.Meta = attr_meta |
123 | 123 | return new_class |
124 | 124 | |
125 | | if old_default_mgr and not new_class._default_manager: |
126 | | new_class._default_manager = old_default_mgr._copy_to_model(new_class) |
127 | 125 | new_class._prepare() |
128 | 126 | register_models(new_class._meta.app_label, new_class) |
129 | 127 | |
diff --git a/django/db/models/manager.py b/django/db/models/manager.py
index 3a9da34..dfb2dc5 100644
a
|
b
|
class Manager(object):
|
31 | 31 | |
32 | 32 | def contribute_to_class(self, model, name): |
33 | 33 | # TODO: Use weakref because of possible memory leak / circular reference. |
34 | | self.model = model |
35 | | setattr(model, name, ManagerDescriptor(self)) |
36 | | if not getattr(model, '_default_manager', None) or self.creation_counter < model._default_manager.creation_counter: |
37 | | model._default_manager = self |
38 | | |
39 | | def _copy_to_model(self, model): |
40 | | """ |
41 | | Makes a copy of the manager and assigns it to 'model', which should be |
42 | | a child of the existing model (used when inheriting a manager from an |
43 | | abstract base class). |
44 | | """ |
45 | | assert issubclass(model, self.model) |
46 | | mgr = copy.copy(self) |
47 | | mgr.model = model |
48 | | return mgr |
| 34 | if not model._meta.abstract: |
| 35 | self.model = model |
| 36 | setattr(model, name, ManagerDescriptor(self)) |
| 37 | if not getattr(model, '_default_manager', None) or self.creation_counter == model._default_manager.creation_counter: |
| 38 | model._default_manager = self |
| 39 | model._meta.base_managers[name] = self |
49 | 40 | |
50 | 41 | ####################### |
51 | 42 | # PROXIES TO QUERYSET # |
diff --git a/django/db/models/options.py b/django/db/models/options.py
index 5802ead..d1feed9 100644
a
|
b
|
class Options(object):
|
44 | 44 | self.one_to_one_field = None |
45 | 45 | self.abstract = False |
46 | 46 | self.parents = SortedDict() |
| 47 | self.base_managers = SortedDict() |
47 | 48 | |
48 | 49 | def contribute_to_class(self, cls, name): |
49 | 50 | cls._meta = self |
diff --git a/tests/modeltests/model_inheritance/models.py b/tests/modeltests/model_inheritance/models.py
index b1a751f..0f959a7 100644
a
|
b
|
from django.db import models
|
18 | 18 | # Abstract base classes |
19 | 19 | # |
20 | 20 | |
| 21 | class AdultManager(models.Manager): |
| 22 | def get_query_set(self): |
| 23 | return super(AdultManager, self).get_query_set().filter(age__gte=18) |
| 24 | |
| 25 | class Adult21Manager(models.Manager): |
| 26 | def get_query_set(self): |
| 27 | return super(Adult21Manager, self).get_query_set().filter(age__gte=21) |
| 28 | |
21 | 29 | class CommonInfo(models.Model): |
22 | 30 | name = models.CharField(max_length=50) |
23 | 31 | age = models.PositiveIntegerField() |
24 | 32 | |
| 33 | objects = models.Manager() |
| 34 | adults = AdultManager() |
| 35 | |
25 | 36 | class Meta: |
26 | 37 | abstract = True |
27 | 38 | ordering = ['name'] |
… |
… |
class CommonInfo(models.Model):
|
30 | 41 | return u'%s %s' % (self.__class__.__name__, self.name) |
31 | 42 | |
32 | 43 | class Worker(CommonInfo): |
| 44 | adults = Adult21Manager() |
| 45 | |
33 | 46 | job = models.CharField(max_length=50) |
34 | 47 | |
35 | 48 | class Student(CommonInfo): |
… |
… |
__test__ = {'API_TESTS':"""
|
102 | 115 | |
103 | 116 | >>> w = Worker(name='Fred', age=35, job='Quarry worker') |
104 | 117 | >>> w.save() |
105 | | >>> w2 = Worker(name='Barney', age=34, job='Quarry worker') |
| 118 | >>> w2 = Worker(name='Barney', age=20, job='Quarry worker') |
106 | 119 | >>> w2.save() |
107 | 120 | >>> s = Student(name='Pebbles', age=5, school_class='1B') |
108 | 121 | >>> s.save() |
… |
… |
u'Student Pebbles'
|
121 | 134 | >>> Student._meta.ordering |
122 | 135 | [] |
123 | 136 | |
| 137 | # The children inherit the custom managers from their parents, but children can |
| 138 | # still override it. |
| 139 | >>> Student(name='Bill', age=18, school_class='11A').save() |
| 140 | >>> Student.adults.all() |
| 141 | [<Student: Student Bill>] |
| 142 | |
| 143 | >>> Worker.adults.all() |
| 144 | [<Worker: Worker Fred>] |
| 145 | |
124 | 146 | # However, the CommonInfo class cannot be used as a normal model (it doesn't |
125 | 147 | # exist as a model). |
126 | 148 | >>> CommonInfo.objects.all() |