Ticket #7154: 0004-Now-all-managers-not-only-the-default-manager-gets.patch
File 0004-Now-all-managers-not-only-the-default-manager-gets.patch, 9.9 KB (added by , 16 years ago) |
---|
-
django/db/models/base.py
From b6a2858190975e89a04dcc3a1aceafaf8be95860 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 | 31 ++++++++++++----------- django/db/models/options.py | 1 + tests/modeltests/custom_managers/models.py | 35 +++++++++++++++++++++---- tests/modeltests/model_inheritance/models.py | 24 +++++++++++++++++- 5 files changed, 76 insertions(+), 31 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 = None69 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_manager73 new_class._default_manager = None74 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 -
django/db/models/manager.py
diff --git a/django/db/models/manager.py b/django/db/models/manager.py index 3a9da34..912e731 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 # If the creation_counter is less than the creation_counter of 39 # the default manager, it is defined before the given default 40 # manager, so we set the default manager, that when all managers 41 # were contributed to the model, the first defined is the 42 # default manager. 43 44 # If the creation_counter is equals the creation_counter of the 45 # default manager, it is the same manager in a derived model and 46 # we have to update it, because of its model attribute is 47 # incorrect now. 48 model._default_manager = self 49 model._meta.base_managers[name] = self 49 50 50 51 ####################### 51 52 # PROXIES TO QUERYSET # -
django/db/models/options.py
diff --git a/django/db/models/options.py b/django/db/models/options.py index 5802ead..a855966 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 = {} 47 48 48 49 def contribute_to_class(self, cls, name): 49 50 cls._meta = self -
tests/modeltests/custom_managers/models.py
diff --git a/tests/modeltests/custom_managers/models.py b/tests/modeltests/custom_managers/models.py index 40bf77e..9425a2e 100644
a b class Book(models.Model): 44 44 45 45 # An example of providing multiple custom managers. 46 46 47 class SlowCarManager(models.Manager): 48 def get_query_set(self): 49 return super(SlowCarManager, self).get_query_set().filter(top_speed__lte=124) 50 51 class MediumFastCarManager(models.Manager): 52 def get_query_set(self): 53 return super(MediumFastCarManager, self).get_query_set().filter(top_speed__range=(125, 149)) 54 47 55 class FastCarManager(models.Manager): 48 56 def get_query_set(self): 49 return super(FastCarManager, self).get_query_set().filter(top_speed__gt=150) 57 return super(FastCarManager, self).get_query_set().filter(top_speed__range=(150, 174)) 58 59 class VeryFastCarManager(models.Manager): 60 def get_query_set(self): 61 return super(VeryFastCarManager, self).get_query_set().filter(top_speed__range=(175, 199)) 62 63 class ReallyFastCarManager(models.Manager): 64 def get_query_set(self): 65 return super(ReallyFastCarManager, self).get_query_set().filter(top_speed__gte=200) 50 66 51 67 class Car(models.Model): 52 68 name = models.CharField(max_length=10) 53 69 mileage = models.IntegerField() 54 70 top_speed = models.IntegerField(help_text="In miles per hour.") 71 72 # We need a large number of managers to see whether it is really the first 73 # defined, which becomes the default manager, due to python's dict internal. 55 74 cars = models.Manager() 75 slow_cars = SlowCarManager() 76 medium_fast_cars = MediumFastCarManager() 56 77 fast_cars = FastCarManager() 78 very_fast_cars = VeryFastCarManager() 79 really_fast_cars = ReallyFastCarManager() 57 80 58 81 def __unicode__(self): 59 82 return self.name … … True 90 113 91 114 >>> Book.published_objects.all() 92 115 [<Book: How to program>] 93 94 116 >>> c1 = Car(name='Corvette', mileage=21, top_speed=180) 95 117 >>> c1.save() 96 118 >>> c2 = Car(name='Neon', mileage=31, top_speed=100) 97 119 >>> c2.save() 98 120 >>> Car.cars.order_by('name') 99 121 [<Car: Corvette>, <Car: Neon>] 100 >>> Car. fast_cars.all()122 >>> Car.very_fast_cars.all() 101 123 [<Car: Corvette>] 102 124 103 125 # Each model class gets a "_default_manager" attribute, which is a reference 104 # to the first manager defined in the class. In this case, it's "cars". 105 >>> Car._default_manager.order_by('name') 106 [<Car: Corvette>, <Car: Neon>] 126 # to the first manager defined in the class. 127 >>> Car._default_manager 128 <django.db.models.manager.Manager object at ...> 129 107 130 """} -
tests/modeltests/model_inheritance/models.py
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()