Code

Ticket #17270: manager.py

File manager.py, 4.3 KB (added by Wojciech Banaś <fizista@…>, 2 years ago)

New manager class

Line 
1from types import MethodType
2from django.db import models
3from django.db.models.query import QuerySet
4
5def applicate_method_list(queryset_object):
6    """
7    Convert method list to object attribute.
8    """
9    manager_method_list = getattr(queryset_object, '_manager_method_list')
10    for query_method in manager_method_list:
11        try:
12            getattr(queryset_object, query_method.__name__)
13            break
14        except AttributeError:
15            setattr(queryset_object, query_method.__name__, query_method)
16    return queryset_object
17
18def manager_methods_in_queryset(_clone_method):
19    """
20    Decorator for method '_clone' in object QuerySet
21    """
22    def _clone_decorator(*args, **kwargs):
23        c = _clone_method(*args, **kwargs)
24        if not hasattr(c, '_manager_method_list') and hasattr(args[0], '_manager_method_list'):
25            c._manager_method_list = getattr(args[0], '_manager_method_list')
26            applicate_method_list(c)
27        return c
28    return _clone_decorator
29
30class ManagerQuerySetDecorator(models.Manager):
31    """
32    Extends the operation methods of the manager on subqueries QuerySet objects.
33   
34    The "standard manager" you can only like this:
35    SomeModel.object.some_method_manager().all().filter(...). ...
36   
37    But this does not work:
38    SomeModel.object.all().some_method_manager()
39   
40    And this it works with the use of this class / patches:
41    SomeModel.object.all().some_method_manager().filter(...).some_method_manager2(). ...
42    SomeModel.object.some_method_manager().some_method_manager2().some_method_manager3(). ...
43   
44    Extension of the methods manager on all child objects "QuerySeth".
45   
46    Example:
47   
48    class Animals(models.Model):
49        type = models.CharField('Type of animal') # dog, cat, elephant
50        color = models.CharField('Colour Name')
51       
52        objects = AnimalsManager()
53       
54    class AnimalsManager(ManagerQuerySetDecorator):
55   
56        def get_dogs(self):
57            return self.get_query_set().filter(type='dog')
58        get_dogs.queryset_method = True
59       
60        def get_cats(self):
61            return self.get_query_set().filter(type='cat')
62        get_cats.queryset_method = True
63       
64        def get_white_animals(self):
65            return self.get_query_set().filter(color='white')
66        get_white_animals.queryset_method = True
67       
68        def get_black_animals(self):
69            return self.get_query_set().filter(color='black')
70        get_black_animals.queryset_method = True
71       
72        def get_brown_animals(self):
73            return self.get_query_set().filter(color='black')
74
75       
76    Animals(type='dog', color='black').save()
77    Animals(type='dog', color='bown').save()
78    Animals(type='dog', color='white').save()
79    Animals(type='cat', color='black').save()
80    Animals(type='cat', color='bown').save()
81    Animals(type='cat', color='white').save()
82   
83    animals = Animals.objects.get_black_animals().get_dogs()
84    # return list black dogs
85
86    animals = Animals.objects.get_white_animals().get_cats()
87    # return list white cats
88   
89    # When ffff equals False, or not defined (as it is in
90    # the original model manager), we can not perform such an operation:
91    animals = Animals.objects.get_cats().get_brown_animals()
92    # return:
93    # AttributeError: 'QuerySet' object has no attribute 'get_brown_animals'
94   
95    # but:
96    animals = Animals.objects.get_brown_animals().get_cats()
97    # return brown cats, because get_cat() present in all parent object instances.
98    """
99    def __init__(self):
100        super(ManagerQuerySetDecorator, self).__init__()
101        # Method _clone decoration if it was not previously decorated
102        if not getattr(QuerySet._clone, '__name__') == '_clone_decorator':
103            QuerySet._clone = manager_methods_in_queryset(QuerySet._clone)
104   
105    def get_query_set(self):
106        qs = super(ManagerQuerySetDecorator, self).get_query_set()
107        list_arrtibutes = [getattr(self,attrib_name) for attrib_name in dir(self)]
108        for attr in list_arrtibutes:
109            if isinstance(attr, MethodType) and hasattr(attr, 'queryset_method') and  attr.queryset_method:
110                try:         
111                    qs._manager_method_list += [attr]
112                except AttributeError:
113                    qs._manager_method_list = [attr]
114        applicate_method_list(qs)
115        return qs