Django

Code

Changeset 8851

Show
Ignore:
Timestamp:
09/02/08 04:04:54 (3 months ago)
Author:
mtredinnick
Message:

Fixed #7154 -- Inherit all model managers from abstract base classes.
Also added documentation describing how manager inheritance works (and when
manager aren't inherited). Based on some patches from sebastian_noack and
emulbreh.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/db/models/base.py

    r8674 r8851  
    6868                    new_class._meta.get_latest_by = base_meta.get_latest_by 
    6969 
    70         old_default_mgr = None 
    7170        if getattr(new_class, '_default_manager', None): 
    72             # We have a parent who set the default manager. 
    73             if new_class._default_manager.model._meta.abstract: 
    74                 old_default_mgr = new_class._default_manager 
    7571            new_class._default_manager = None 
    7672 
     
    112108                    new_class.add_to_class(field.name, copy.deepcopy(field)) 
    113109 
     110            # Inherit managers from the abstract base classes. 
     111            base_managers = base._meta.abstract_managers 
     112            base_managers.sort() 
     113            for _, mgr_name, manager in base_managers: 
     114                val = getattr(new_class, mgr_name, None) 
     115                if not val or val is manager: 
     116                    new_manager = manager._copy_to_model(new_class) 
     117                    new_class.add_to_class(mgr_name, new_manager) 
    114118        if abstract: 
    115119            # Abstract base models can't be instantiated and don't appear in 
     
    120124            return new_class 
    121125 
    122         if old_default_mgr and not new_class._default_manager: 
    123             new_class._default_manager = old_default_mgr._copy_to_model(new_class) 
    124126        new_class._prepare() 
    125127        register_models(new_class._meta.app_label, new_class) 
  • django/trunk/django/db/models/manager.py

    r8223 r8851  
    2424    def __init__(self): 
    2525        super(Manager, self).__init__() 
    26         # Increase the creation counter, and save our local copy. 
    27         self.creation_counter = Manager.creation_counter 
    28         Manager.creation_counter += 1 
     26        self._set_creation_counter() 
    2927        self.model = None 
     28        self._inherited = False 
    3029 
    3130    def contribute_to_class(self, model, name): 
     
    3534        if not getattr(model, '_default_manager', None) or self.creation_counter < model._default_manager.creation_counter: 
    3635            model._default_manager = self 
     36        if model._meta.abstract or self._inherited: 
     37            model._meta.abstract_managers.append((self.creation_counter, name, 
     38                    self)) 
     39 
     40    def _set_creation_counter(self): 
     41        """ 
     42        Sets the creation counter value for this instance and increments the 
     43        class-level copy. 
     44        """ 
     45        self.creation_counter = Manager.creation_counter 
     46        Manager.creation_counter += 1 
    3747 
    3848    def _copy_to_model(self, model): 
     
    4454        assert issubclass(model, self.model) 
    4555        mgr = copy.copy(self) 
     56        mgr._set_creation_counter() 
    4657        mgr.model = model 
     58        mgr._inherited = True 
    4759        return mgr 
    4860 
  • django/trunk/django/db/models/options.py

    r8730 r8851  
    4545        self.parents = SortedDict() 
    4646        self.duplicate_targets = {} 
     47        # Managers that have been inherited from abstract base classes. These 
     48        # are passed onto any children. 
     49        self.abstract_managers = [] 
    4750 
    4851    def contribute_to_class(self, cls, name): 
  • django/trunk/docs/topics/db/managers.txt

    r8506 r8851  
    190190 
    191191        ... 
     192 
     193Custom managers and model inheritance 
     194------------------------------------- 
     195 
     196Class inheritance and model managers aren't quite a perfect match for each 
     197other. Managers are often specific to the classes they are defined on and 
     198inheriting them in subclasses isn't necessarily a good idea. Also, because the 
     199first manager declared is the *default manager*, it is important to allow that 
     200to be controlled. So here's how Django handles custom managers and 
     201:ref:`model inheritance <model-inheritance>`: 
     202 
     203    1. Managers defined on non-abstract base classes are *not* inherited by 
     204       child classes. If you want to reuse a manager from a non-abstract base, 
     205       redeclare it explicitly on the child class. These sorts of managers are 
     206       likely to be fairly specific to the class they are defined on, so 
     207       inheriting them can often lead to unexpected results (particularly as 
     208       far as the default manager goes). Therefore, they aren't passed onto 
     209       child classes. 
     210 
     211    2. Managers from abstract base classes are always inherited by the child 
     212       class, using Python's normal name resolution order (names on the child 
     213       class override all others; then come names on the first parent class, 
     214       and so on). Abstract base classes are designed to capture information 
     215       and behaviour that is common to their child classes. Defining common 
     216       managers is an appropriate part of this common information. 
     217 
     218    3. The default manager on a class is either the first manager declared on 
     219       the class, if that exists, or the default manager of the first abstract 
     220       base class in the parent hierarchy, if that exists. If no default 
     221       manager is explicitly declared, Django's normal default manager is 
     222       used. 
     223