from django.core.exceptions import ValidationError as DjangoValidationError
from rest_framework.exceptions import ValidationError
from rest_framework.filters import BaseFilterBackend
from ..filtersets import ModelFilterSet
[docs]class DRFFilterBackend(BaseFilterBackend):
"""
DRF filter backend which integrates with ``django-ufilter``
This integration backend can be specified in global DRF settings::
# settings.py
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': [
'django_ufilter.integrations.drf.DRFFilterBackend',
]
}
Alternatively filter backend can be specified per view/viewset bases::
class MyViewSet(ModelViewSet):
queryset = MyModel.objects.all()
filter_backends = [DRFFilterBackend]
filter_fields = ['field1', 'field2']
The following attributes can be specified on the view:
* ``filter_class`` - explicit filter (:class:`.FilterSet` to be specific) class
which should be used for filtering. When this attribute is supplied, this
filterset will be used and all other attributes described below will are ignored.
* ``filter_fields`` - list of strings which should be names
of fields which should be included in the generated :class:`.FilterSet`.
This is equivalent::
class MyFilterSet(ModelFilterSet):
class Meta(object):
model = MyModel
fields = ['fields1', ...]
* ``filter_class_meta_kwargs`` - additional kwargs which should be passed
in ``Meta`` for the generated :class:`.FilterSet`.
* ``filter_class_default`` - base class to use while creating a
new :class:`.FilterSet`.
This is primarily useful when using non-Django data-sources.
By default :attr:`.default_filter_set` is used.
"""
default_filter_set = ModelFilterSet
"""
Default base class which will be used while dynamically creating :class:`.FilterSet`
"""
[docs] def get_filter_class(self, view, queryset=None):
"""
Get filter class which will be used for filtering.
Parameters
----------
view : View
DRF view/viewset where this filter backend is being used.
Please refer to :class:`.DRFFilterBackend` documentation
for list of attributes which can be supplied in view
to customize how filterset will be determined.
queryset
Query object for filtering
Returns
-------
:class:`.FilterSet`
:class:`.FilterSet` class either directly specified in the view or
dynamically constructed for the queryset model.
None
When appropriate :class:`.FilterSet` cannot be determined
for filtering
"""
filter_class_default = getattr(
view, "filter_class_default", self.default_filter_set
)
filter_class = getattr(view, "filter_class", None)
filter_class_meta_kwargs = getattr(view, "filter_class_meta_kwargs", {})
filter_fields = getattr(view, "filter_fields", None)
if filter_class:
return filter_class
if filter_fields:
model = filter_class_default.filter_backend_class(queryset).get_model()
meta_kwargs = filter_class_meta_kwargs.copy()
meta_kwargs.update({"model": model, "fields": filter_fields})
meta = type(str("Meta"), (object,), meta_kwargs)
return type(
str("{}FilterSet".format(model.__name__)),
(filter_class_default,),
{"Meta": meta},
)
[docs] def get_filter_context(self, request, view):
"""
Get context to be passed to :class:`.FilterSet` during initialization
Parameters
----------
request : HttpRequest
Request object from the view
view : View
View where this filter backend is being used
Returns
-------
dict
Context to be passed to :class:`.FilterSet`
"""
return {"request": request, "view": view}
[docs] def filter_queryset(self, request, queryset, view):
"""
Main method for filtering query object
Parameters
----------
request : HttpRequest
Request object from the view
queryset
Query object for filtering
view : View
View where this filter backend is being used
Returns
-------
object
Filtered query object if filtering class was determined by
:meth:`.get_filter_class`. If not given ``queryset`` is returned.
"""
filter_class = self.get_filter_class(view, queryset)
if filter_class:
_filter = filter_class(
data=request.query_params,
queryset=queryset,
context=self.get_filter_context(request, view),
)
filter_model = getattr(_filter.Meta, "model", None)
if filter_model and _filter.filter_backend.enforce_same_models:
model = _filter.filter_backend.model
assert issubclass(
model, filter_model
), "FilterSet model {} does not match queryset model {}" "".format(
filter_model, model
)
try:
return _filter.filter()
except DjangoValidationError as e:
raise ValidationError(e.message_dict)
return queryset
URLFilterBackend = DRFFilterBackend