Skip to content

Commit 46566ab

Browse files
authored
Merge pull request #27 from adesor/get-queryset-mixin
Eliminate the need for overriding get_queryset() for filtering logic
2 parents 2fc13df + 53a493b commit 46566ab

File tree

4 files changed

+44
-24
lines changed

4 files changed

+44
-24
lines changed

README.md

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ class PlayersViewSet(FiltersMixin, viewsets.ModelViewSet):
7979
This viewset automatically provides `list`, `create`, `retrieve`,
8080
`update` and `destroy` actions.
8181
"""
82+
queryset = Player.objects.prefetch_related(
83+
'teams' # use prefetch_related to minimize db hits.
84+
).all()
8285
serializer_class = PlayerSerializer
8386
pagination_class = ResultSetPagination
8487
filter_backends = (filters.OrderingFilter,)
@@ -103,30 +106,6 @@ class PlayersViewSet(FiltersMixin, viewsets.ModelViewSet):
103106

104107
# add validation on filters
105108
filter_validation_schema = players_query_schema
106-
107-
def get_queryset(self):
108-
"""
109-
Optionally restricts the queryset by filtering against
110-
query parameters in the URL.
111-
"""
112-
query_params = self.request.query_params
113-
url_params = self.kwargs
114-
115-
# get queryset_filters from FilterMixin
116-
queryset_filters = self.get_db_filters(url_params, query_params)
117-
118-
# This dict will hold filter kwargs to pass in to Django ORM calls.
119-
db_filters = queryset_filters['db_filters']
120-
121-
# This dict will hold exclude kwargs to pass in to Django ORM calls.
122-
db_excludes = queryset_filters['db_excludes']
123-
124-
# fetch queryset from Players model
125-
queryset = Player.objects.prefetch_related(
126-
'teams' # use prefetch_related to minimize db hits.
127-
).all()
128-
129-
return queryset.filter(**db_filters).exclude(**db_excludes)
130109
```
131110

132111
With the use of `drf-url-filters` adding a new filter on a new column is as

filters/decorators.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
2+
def decorate_get_queryset(f):
3+
def decorated(self):
4+
queryset = f(self)
5+
query_params = self.request.query_params
6+
url_params = self.kwargs
7+
8+
# get queryset_filters from FiltersMixin
9+
queryset_filters = self.get_db_filters(url_params, query_params)
10+
11+
# This dict will hold filter kwargs to pass in to Django ORM calls.
12+
db_filters = queryset_filters['db_filters']
13+
14+
# This dict will hold exclude kwargs to pass in to Django ORM calls.
15+
db_excludes = queryset_filters['db_excludes']
16+
17+
return queryset.filter(**db_filters).exclude(**db_excludes)
18+
return decorated

filters/metaclasses.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from .decorators import decorate_get_queryset
2+
3+
4+
class MetaFiltersMixin(type):
5+
def __new__(cls, name, bases, dct):
6+
if 'get_queryset' in dct:
7+
dct['get_queryset'] = decorate_get_queryset(dct['get_queryset'])
8+
return super(MetaFiltersMixin, cls).__new__(cls, name, bases, dct)
9+
10+
def __setattr__(self, attr, val):
11+
if attr == 'get_queryset':
12+
val = decorate_get_queryset(val)
13+
return super(MetaFiltersMixin, self).__setattr__(attr, val)

filters/mixins.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from voluptuous import Invalid
33
from rest_framework.exceptions import ParseError
44

5+
from .metaclasses import MetaFiltersMixin
56
from .schema import base_query_params_schema
67

78

@@ -11,6 +12,7 @@ class FiltersMixin(object):
1112
filters by applying defined filters on generic
1213
queryset.
1314
'''
15+
__metaclass__ = MetaFiltersMixin
1416

1517
def __get_queryset_filters(self, query_params, *args, **kwargs):
1618
'''
@@ -87,3 +89,11 @@ def get_db_filters(self, url_params, query_params):
8789
'db_filters': db_filters,
8890
'db_excludes': db_excludes,
8991
}
92+
93+
def get_queryset(self):
94+
# Defined here to handle the case where the viewset
95+
# does not override get_queryset
96+
# (and hence the metaclass would not have been
97+
# able to decorate it with the filtering logic.)
98+
99+
return super(FiltersMixin, self).get_queryset()

0 commit comments

Comments
 (0)