Login

Tatsypie: additional list endpoints for custom Model's manager methods

Author:
migajek
Posted:
November 15, 2012
Language:
Python
Version:
1.4
Score:
0 (after 0 ratings)

Although configuring filtering in TastyPie is possible, it is limited to per-field filters, which are not enough for more complex filtering.

If your model implement custom manager methods for complex filters, exposing these methods as TastyPie Resource list endpoints is not an easy task.

The ModelResource subclass provided here does this, providing 3 ways of complex filtering: define querysets for filters directly in the Resource declaration use Model manager custom method * use QuerySet custom method

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# by Michal migajek Gajek

class NBResource(ModelResource):
    class Meta:        
        abstract = True
        
        # there are three options for specifying filters.
        # it's either a dictionary of 'filter_name': queryset
        # or a tuple of filter names
        # in the second case, if the default_manager attribute is provided
        # the filter name is assumed to be Manager method, 
        # otherwise it's assumed to be QuerySet method
        default_manager = None 
        manager_filters = ()
        
    def prepend_urls(self):
        """ if there are allowed custom Manager methods, add special url for that"""
        return [
                 url(r"^(?P<resource_name>%s)/filtered_list/(?P<filter>\w+)%s$" % (self._meta.resource_name, trailing_slash()), self.wrap_view('dispatch_list_filtered'), name="api_dispatch_list_filtered"),
        ] if self._meta.manager_filters else []
        
    def dispatch_list_filtered(self, request, **kwargs):
        """ check if the provided filter name is valid, and - if so - proceed to the get_object_list """ 
        mfilter = kwargs.pop('filter')
        filters = self._meta.manager_filters        
        if (isinstance(filters, dict) and not mfilter in filters.keys()) or \
            not mfilter in filters:
                raise Exception('Invalid filter (%s) name provided' % filter)
                
        request.custom_filter = mfilter
        return self.dispatch_list(request)
        
    def get_object_list(self, request):        
        """ applies custom filtering if the filter name was provided """
        if hasattr(request, 'custom_filter'):
            filters = self._meta.manager_filters
            if isinstance(filters, dict):
                # filter to apply is in fact a name of queryset specified in Resource 
                queryset = filters[request.custom_filter]._clone()
            else:
                # if there's a default_manager, filter is it's method
                # otherwise we assume filter is a method of default QuerySet
                manager_or_queryset = self._meta.default_manager or super(NBResource, self).get_object_list(request)
                method = getattr(manager_or_queryset, request.custom_filter)
                if not method:
                    raise Exception('Manager or QuerySet does not have method named %s' % request.custom_filter)
                
                #FIXME: very, very ugly trick...                
                kwargs = request.GET.dict()
                if 'format' in kwargs.keys():
                    kwargs = deepcopy(kwargs)
                    kwargs.pop('format')
                queryset = method(**kwargs) 
        else:
            queryset = super(NBResource, self).get_object_list(request)
        return queryset








#### example usage

# scenario one - simple - define filters as querysets directly in the ModelResource
class FooResource(NBResource)
    class Meta:
        queryset = Foo.objects.all() #standard definition for TastyPie
        manager_filters = {'active': Foo.objects.filter(active = True) }
# calling /api/foo/filtered_list/active 
# will return only items with active = True attribute
        



# scenario two - complex filtering based on custom Manager's method
class FooManager(models.Manager)
    def recent_active_items(some_parameter)
        return filteredQuerySet

class Foo(models.Model)
   ...
   objects = FooManager()


class FooResource(NBResource)
    class Meta:
        queryset = Foo.objects.all() #standard definition for TastyPie
        manager_filters = ('recent_active_items',)
        default_manager = Foo.objects 
# calling /api/foo/filtered_list/recent_active_items?some_parameter=value
# will call FooManager's method with some_parameter as keyword argument

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 2 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 2 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 9 months, 2 weeks ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 10 months, 1 week ago
  5. Help text hyperlinks by sa2812 11 months ago

Comments

Please login first before commenting.