Skip to content

Commit e138e09

Browse files
authored
add support to excludes and filters results based upon query params (#15)
1 parent fb3eed4 commit e138e09

File tree

4 files changed

+90
-50
lines changed

4 files changed

+90
-50
lines changed

docs/README.md

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -90,22 +90,23 @@ class PlayersViewSet(FiltersMixin, viewsets.ModelViewSet):
9090
query parameters in the URL.
9191
"""
9292
query_params = self.request.query_params
93-
queryset = Player.objects.prefetch_related(
94-
'teams' # use prefetch_related to minimize db hits.
95-
).all()
93+
url_params = self.kwargs
94+
95+
# get queryset_filters from FilterMixin
96+
queryset_filters = self.get_db_filters(url_params, query_params)
9697

9798
# This dict will hold filter kwargs to pass in to Django ORM calls.
98-
db_filters = {}
99+
db_filters = queryset_filters['db_filters']
99100

100-
# update filters dict with incoming query params and then pass as
101-
# **kwargs to queryset.filter()
102-
db_filters.update(
103-
self.get_queryset_filters(
104-
query_params
105-
)
106-
)
107-
return queryset.filter(**db_filters)
101+
# This dict will hold exclude kwargs to pass in to Django ORM calls.
102+
db_excludes = queryset_filters['db_excludes']
103+
104+
# fetch queryset from Players model
105+
queryset = Player.objects.prefetch_related(
106+
'teams' # use prefetch_related to minimize db hits.
107+
).all()
108108

109+
return queryset.filter(**db_filters).exclude(**db_excludes)
109110
```
110111

111112
With the use of `drf-url-filters` adding a new filter on a new column is as simple as adding a new key in the dict. Prohibitting a filter on particular column is same as removing a key value mapping from the `filter_mappings` dict.
@@ -117,7 +118,7 @@ Copyright (c) 2016 Manjit Kumar
117118
Read more about it in LICENSE file available in repo.
118119

119120
# Credits
120-
Special thanks to authors of [voluptouos](https://github.com/alecthomas/voluptuous) and friends [*](https://github.com/cdax) [**](https://github.com/SaurabhJha) who encourage people to contribute into open source community.
121+
Special thanks to authors of [voluptouos](https://github.com/alecthomas/voluptuous) and friends [saurabhjha](https://github.com/cdax) [cdax](https://github.com/SaurabhJha) who encourage people to contribute into open source community.
121122

122123
# Support
123124
Please [open an issue](https://github.com/manjitkumar/drf-url-filters/issues/new) for support.

example_app/views.py

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -43,21 +43,23 @@ def get_queryset(self):
4343
query parameters in the URL.
4444
"""
4545
query_params = self.request.query_params
46+
url_params = self.kwargs
47+
48+
# get queryset_filters from FilterMixin
49+
queryset_filters = self.get_db_filters(url_params, query_params)
50+
51+
# This dict will hold filter kwargs to pass in to Django ORM calls.
52+
db_filters = queryset_filters['db_filters']
53+
54+
# This dict will hold exclude kwargs to pass in to Django ORM calls.
55+
db_excludes = queryset_filters['db_excludes']
56+
57+
# fetch queryset from Players model
4658
queryset = Player.objects.prefetch_related(
4759
'teams' # use prefetch_related to minimize db hits.
4860
).all()
4961

50-
# This dict will hold filter kwargs to pass in to Django ORM calls.
51-
db_filters = {}
52-
53-
# update filters dict with incoming query params and then pass as
54-
# **kwargs to queryset.filter()
55-
db_filters.update(
56-
self.get_queryset_filters(
57-
query_params
58-
)
59-
)
60-
return queryset.filter(**db_filters)
62+
return queryset.filter(**db_filters).exclude(**db_excludes)
6163

6264

6365
class TeamsViewSet(FiltersMixin, viewsets.ModelViewSet):
@@ -90,18 +92,21 @@ def get_queryset(self):
9092
Optionally restricts the queryset by filtering against
9193
query parameters in the URL.
9294
"""
95+
9396
query_params = self.request.query_params
97+
url_params = self.kwargs
98+
99+
# get queryset_filters from FilterMixin
100+
queryset_filters = self.get_db_filters(url_params, query_params)
101+
102+
# This dict will hold filter kwargs to pass in to Django ORM calls.
103+
db_filters = queryset_filters['db_filters']
104+
105+
# This dict will hold exclude kwargs to pass in to Django ORM calls.
106+
db_excludes = queryset_filters['db_excludes']
107+
94108
queryset = Team.objects.prefetch_related(
95109
'players'
96110
).all()
97111

98-
# This dict will hold filter kwargs to pass in to Django ORM calls.
99-
db_filters = {}
100-
101-
# filters on mercant queryset
102-
db_filters.update(
103-
self.get_queryset_filters(
104-
query_params
105-
)
106-
)
107-
return queryset.filter(**db_filters)
112+
return queryset.filter(**db_filters).exclude(**db_excludes)

filters/mixins.py

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,38 +9,72 @@ class FiltersMixin(object):
99
queryset.
1010
'''
1111

12-
def get_queryset_filters(self, query_params, *args, **kwargs):
12+
def __get_queryset_filters(self, query_params, *args, **kwargs):
1313
'''
1414
get url_params and query_params and make db_filters
1515
to filter the queryset to the finest.
16-
17-
[1] when a CSV is passed as value to a query params make a filter
16+
[1] ~ sign is used to negated / exclude a filter.
17+
[2] when a CSV is passed as value to a query params make a filter
1818
with 'IN' query.
1919
'''
2020

2121
filters = []
22+
excludes = []
23+
2224
if getattr(self, 'filter_mappings', None) and query_params:
2325
filter_mappings = self.filter_mappings
2426

2527
try:
2628
# check and raise 400_BAD_REQUEST for invalid query params
27-
if getattr(self, 'filter_validation_schema', None):
28-
query_params = self.filter_validation_schema(
29-
self.request.query_params
30-
)
31-
else:
32-
raise Invalid('Validation is not configured for filters.')
29+
filter_validation_schema = getattr(
30+
self,
31+
'filter_validation_schema',
32+
base_query_params_schema
33+
)
34+
query_params = filter_validation_schema(query_params)
3335
except Invalid as inst:
3436
raise ParseError(detail=inst)
3537

36-
filters = []
37-
for query, value in query_params.items():
38+
for query, value in query_params.iteritems():
39+
# [1] ~ sign is used to exclude a filter.
40+
is_exclude = '~' in query
3841
if query in self.filter_mappings and value:
3942
query = filter_mappings[query]
40-
# [1] multiple options is filter values will execute as in query
43+
# [2] multiple options is filter values will execute as `IN` query
4144
if isinstance(value, list):
4245
query += '__in'
4346
if value:
44-
filters.append((query, value))
45-
filters = dict(filters)
46-
return filters
47+
if is_exclude:
48+
excludes.append((query, value))
49+
else:
50+
filters.append((query, value))
51+
52+
return dict(filters), dict(excludes)
53+
54+
def __merge_query_params(url_params, query_params):
55+
'''
56+
merges the url_params dict with query_params query dict and returns
57+
the merged dict.
58+
'''
59+
url_params = {}
60+
for key in query_params:
61+
url_params[key] = query_params.get(key) # get method on query-dict works differently than on dict.
62+
return url_params
63+
64+
def get_db_filters(self, url_params, query_params):
65+
'''
66+
returns a dict with db_filters and db_excludes values which can be
67+
used to apply on viewsets querysets.
68+
'''
69+
70+
# merge url and query params
71+
query_params = self.__merge_query_params(url_params, query_params)
72+
73+
# get queryset filters
74+
db_filters = self.__get_queryset_filters(query_params)[0]
75+
db_excludes = self.__get_queryset_filters(query_params)[1]
76+
77+
return {
78+
'db_filters': db_filters,
79+
'db_excludes': db_excludes,
80+
}

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)))
2020

2121
__name__ = 'drf-url-filters'
22-
__version__ = '0.1.4'
22+
__version__ = '0.2.0'
2323
__author__ = 'Manjit Kumar'
2424
__author_email__ = 'manjit1727@gmail.com'
2525
__url__ = 'https://github.com/manjitkumar/drf-url-filters'

0 commit comments

Comments
 (0)