Ticket #7154: 0003-Now-all-managers-not-only-the-default-manager-gets.patch

File 0003-Now-all-managers-not-only-the-default-manager-gets.patch, 6.6 KB (added by Sebastian Noack, 16 years ago)
  • django/db/models/base.py

    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):  
    6565                if not hasattr(meta, 'get_latest_by'):
    6666                    new_class._meta.get_latest_by = base_meta.get_latest_by
    6767
    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
    7468        if getattr(new_class._meta, 'app_label', None) is None:
    7569            # Figure out the app_label by looking one level up.
    7670            # For 'django.contrib.sites.models', this would be 'sites'.
    class ModelBase(type):  
    106100                    new_class.add_to_class(attr_name, field)
    107101                new_class._meta.parents[base] = field
    108102            else:
    109                 # The abstract base class case.
     103                # Copy fields from the abstract base class.
    110104                names = set([f.name for f in new_class._meta.local_fields + new_class._meta.many_to_many])
    111105                for field in base._meta.local_fields + base._meta.local_many_to_many:
    112106                    if field.name in names:
    class ModelBase(type):  
    114108                                % (field.name, name, base.__name__))
    115109                    new_class.add_to_class(field.name, copy.deepcopy(field))
    116110
     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
    117117        if abstract:
    118118            # Abstract base models can't be instantiated and don't appear in
    119119            # the list of models for an app. We do the final setup for them a
    class ModelBase(type):  
    122122            new_class.Meta = attr_meta
    123123            return new_class
    124124
    125         if old_default_mgr and not new_class._default_manager:
    126             new_class._default_manager = old_default_mgr._copy_to_model(new_class)
    127125        new_class._prepare()
    128126        register_models(new_class._meta.app_label, new_class)
    129127
  • django/db/models/manager.py

    diff --git a/django/db/models/manager.py b/django/db/models/manager.py
    index 3a9da34..dfb2dc5 100644
    a b class Manager(object):  
    3131
    3232    def contribute_to_class(self, model, name):
    3333        # 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
    4940
    5041    #######################
    5142    # PROXIES TO QUERYSET #
  • django/db/models/options.py

    diff --git a/django/db/models/options.py b/django/db/models/options.py
    index 5802ead..d1feed9 100644
    a b class Options(object):  
    4444        self.one_to_one_field = None
    4545        self.abstract = False
    4646        self.parents = SortedDict()
     47        self.base_managers = SortedDict()
    4748
    4849    def contribute_to_class(self, cls, name):
    4950        cls._meta = self
  • 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  
    1818# Abstract base classes
    1919#
    2020
     21class AdultManager(models.Manager):
     22    def get_query_set(self):
     23        return super(AdultManager, self).get_query_set().filter(age__gte=18)
     24
     25class Adult21Manager(models.Manager):
     26    def get_query_set(self):
     27        return super(Adult21Manager, self).get_query_set().filter(age__gte=21)
     28
    2129class CommonInfo(models.Model):
    2230    name = models.CharField(max_length=50)
    2331    age = models.PositiveIntegerField()
    2432
     33    objects = models.Manager()
     34    adults = AdultManager()
     35
    2536    class Meta:
    2637        abstract = True
    2738        ordering = ['name']
    class CommonInfo(models.Model):  
    3041        return u'%s %s' % (self.__class__.__name__, self.name)
    3142
    3243class Worker(CommonInfo):
     44    adults = Adult21Manager()
     45
    3346    job = models.CharField(max_length=50)
    3447
    3548class Student(CommonInfo):
    __test__ = {'API_TESTS':"""  
    102115
    103116>>> w = Worker(name='Fred', age=35, job='Quarry worker')
    104117>>> w.save()
    105 >>> w2 = Worker(name='Barney', age=34, job='Quarry worker')
     118>>> w2 = Worker(name='Barney', age=20, job='Quarry worker')
    106119>>> w2.save()
    107120>>> s = Student(name='Pebbles', age=5, school_class='1B')
    108121>>> s.save()
    u'Student Pebbles'  
    121134>>> Student._meta.ordering
    122135[]
    123136
     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
    124146# However, the CommonInfo class cannot be used as a normal model (it doesn't
    125147# exist as a model).
    126148>>> CommonInfo.objects.all()
Back to Top