Skip to content

Instantly share code, notes, and snippets.

@ankitml
Last active April 5, 2022 14:03
Show Gist options
  • Save ankitml/fc8f4cf30ff40e19eae6 to your computer and use it in GitHub Desktop.
Save ankitml/fc8f4cf30ff40e19eae6 to your computer and use it in GitHub Desktop.
Pluggable filters for Django Rest Framework
Accompanies (http://iank.it/pluggable-filters-for-django-rest-framework/)
class FiltersBackendBase:
"""
Base class to create FilterBackends
FilterBackends are complementary classes that can be
used with GenericFilterMixin
"""
def __init__(self, request):
self.request = request
def rule(self):
"""
override this for conditional application of filter
eg. when a viewsets should apply a filter only for list view
"""
return True
class GenericFilterMixin:
def get_q_object(self):
all_keys = self.request.query_params.keys()
if not bool(all_keys):
return None
filter_keys = [key for key in all_keys if key in self.filter_backend.mapping]
q = Q()
for key in filter_keys:
value = self.request.query_params[key]
q = q & getattr(self.filter_backend, self.filter_backend.mapping[key])(value)
return q
def get_queryset(self):
queryset = self.queryset
self.filter_backend = self.filter_class(self.request)
if self.filter_backend.rule():
# apply filters if rule passes..
q_filter = self.get_q_object()
if q_filter:
queryset = queryset.filter(q_filter)
return queryset
class SomeFiltersBackend(FiltersBackendBase):
"""
Filter backend class to compliment GenericFilterMixin from utils/mixin.
"""
mapping = {'owner': 'filter_by_owner',
'catness': 'filter_by_catness',
'context': 'filter_by_context'}
def rule(self):
return resolve(self.request.path_info).url_name == 'pet-owners-list'
def filter_by_catness(self, value):
"""
A simple filter to display owners of pets with high catness, canines excuse.
"""
catness = self.request.query_params.get('catness')
return Q(owner__pet__catness__gt=catness)
def filter_by_owner(self, value):
if value == 'me':
return Q(owner=self.request.user.profile)
elif value.isdigit():
try:
profile = PetOwnerProfile.objects.get(user__id=value)
except PetOwnerProfile.DoesNotExist:
raise ValidationError('Owner does not exist')
return Q(owner=profile)
else:
raise ValidationError('Wrong filter applied with owner')
def filter_by_context(self, value):
"""
value = {"context_type" : "context_id or context_ids separated by comma}
"""
import json
try:
context = json.loads(value)
except json.JSONDecodeError as e:
raise ValidationError(e)
context_type, context_ids = context.items()
context_ids = [int(i) for i in context_ids]
if context_type == 'default':
ids = context_ids
else:
ids = Context.get_ids_by_unsupported_contexts(context_type, context_ids)
else:
raise ValidationError('Wrong context type found')
return Q(context_id__in=ids)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment