Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 14 additions & 13 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,22 +90,23 @@ class PlayersViewSet(FiltersMixin, viewsets.ModelViewSet):
query parameters in the URL.
"""
query_params = self.request.query_params
queryset = Player.objects.prefetch_related(
'teams' # use prefetch_related to minimize db hits.
).all()
url_params = self.kwargs

# get queryset_filters from FilterMixin
queryset_filters = self.get_db_filters(url_params, query_params)

# This dict will hold filter kwargs to pass in to Django ORM calls.
db_filters = {}
db_filters = queryset_filters['db_filters']

# update filters dict with incoming query params and then pass as
# **kwargs to queryset.filter()
db_filters.update(
self.get_queryset_filters(
query_params
)
)
return queryset.filter(**db_filters)
# This dict will hold exclude kwargs to pass in to Django ORM calls.
db_excludes = queryset_filters['db_excludes']

# fetch queryset from Players model
queryset = Player.objects.prefetch_related(
'teams' # use prefetch_related to minimize db hits.
).all()

return queryset.filter(**db_filters).exclude(**db_excludes)
```

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.
Expand All @@ -117,7 +118,7 @@ Copyright (c) 2016 Manjit Kumar
Read more about it in LICENSE file available in repo.

# Credits
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.
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.

# Support
Please [open an issue](https://github.com/manjitkumar/drf-url-filters/issues/new) for support.
47 changes: 26 additions & 21 deletions example_app/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,23 @@ def get_queryset(self):
query parameters in the URL.
"""
query_params = self.request.query_params
url_params = self.kwargs

# get queryset_filters from FilterMixin
queryset_filters = self.get_db_filters(url_params, query_params)

# This dict will hold filter kwargs to pass in to Django ORM calls.
db_filters = queryset_filters['db_filters']

# This dict will hold exclude kwargs to pass in to Django ORM calls.
db_excludes = queryset_filters['db_excludes']

# fetch queryset from Players model
queryset = Player.objects.prefetch_related(
'teams' # use prefetch_related to minimize db hits.
).all()

# This dict will hold filter kwargs to pass in to Django ORM calls.
db_filters = {}

# update filters dict with incoming query params and then pass as
# **kwargs to queryset.filter()
db_filters.update(
self.get_queryset_filters(
query_params
)
)
return queryset.filter(**db_filters)
return queryset.filter(**db_filters).exclude(**db_excludes)


class TeamsViewSet(FiltersMixin, viewsets.ModelViewSet):
Expand Down Expand Up @@ -90,18 +92,21 @@ def get_queryset(self):
Optionally restricts the queryset by filtering against
query parameters in the URL.
"""

query_params = self.request.query_params
url_params = self.kwargs

# get queryset_filters from FilterMixin
queryset_filters = self.get_db_filters(url_params, query_params)

# This dict will hold filter kwargs to pass in to Django ORM calls.
db_filters = queryset_filters['db_filters']

# This dict will hold exclude kwargs to pass in to Django ORM calls.
db_excludes = queryset_filters['db_excludes']

queryset = Team.objects.prefetch_related(
'players'
).all()

# This dict will hold filter kwargs to pass in to Django ORM calls.
db_filters = {}

# filters on mercant queryset
db_filters.update(
self.get_queryset_filters(
query_params
)
)
return queryset.filter(**db_filters)
return queryset.filter(**db_filters).exclude(**db_excludes)
64 changes: 49 additions & 15 deletions filters/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,38 +9,72 @@ class FiltersMixin(object):
queryset.
'''

def get_queryset_filters(self, query_params, *args, **kwargs):
def __get_queryset_filters(self, query_params, *args, **kwargs):
'''
get url_params and query_params and make db_filters
to filter the queryset to the finest.

[1] when a CSV is passed as value to a query params make a filter
[1] ~ sign is used to negated / exclude a filter.
[2] when a CSV is passed as value to a query params make a filter
with 'IN' query.
'''

filters = []
excludes = []

if getattr(self, 'filter_mappings', None) and query_params:
filter_mappings = self.filter_mappings

try:
# check and raise 400_BAD_REQUEST for invalid query params
if getattr(self, 'filter_validation_schema', None):
query_params = self.filter_validation_schema(
self.request.query_params
)
else:
raise Invalid('Validation is not configured for filters.')
filter_validation_schema = getattr(
self,
'filter_validation_schema',
base_query_params_schema
)
query_params = filter_validation_schema(query_params)
except Invalid as inst:
raise ParseError(detail=inst)

filters = []
for query, value in query_params.items():
for query, value in query_params.iteritems():
# [1] ~ sign is used to exclude a filter.
is_exclude = '~' in query
if query in self.filter_mappings and value:
query = filter_mappings[query]
# [1] multiple options is filter values will execute as in query
# [2] multiple options is filter values will execute as `IN` query
if isinstance(value, list):
query += '__in'
if value:
filters.append((query, value))
filters = dict(filters)
return filters
if is_exclude:
excludes.append((query, value))
else:
filters.append((query, value))

return dict(filters), dict(excludes)

def __merge_query_params(url_params, query_params):
'''
merges the url_params dict with query_params query dict and returns
the merged dict.
'''
url_params = {}
for key in query_params:
url_params[key] = query_params.get(key) # get method on query-dict works differently than on dict.
return url_params

def get_db_filters(self, url_params, query_params):
'''
returns a dict with db_filters and db_excludes values which can be
used to apply on viewsets querysets.
'''

# merge url and query params
query_params = self.__merge_query_params(url_params, query_params)

# get queryset filters
db_filters = self.__get_queryset_filters(query_params)[0]
db_excludes = self.__get_queryset_filters(query_params)[1]

return {
'db_filters': db_filters,
'db_excludes': db_excludes,
}
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)))

__name__ = 'drf-url-filters'
__version__ = '0.1.4'
__version__ = '0.2.0'
__author__ = 'Manjit Kumar'
__author_email__ = 'manjit1727@gmail.com'
__url__ = 'https://github.com/manjitkumar/drf-url-filters'
Expand Down