"""
The ``post_filter`` filtering backend.
"""
from django_elasticsearch_dsl import fields
from six import string_types
from ...compat import coreapi
from ...compat import coreschema
from ...constants import ALL_LOOKUP_FILTERS_AND_QUERIES
from .common import FilteringFilterBackend
__title__ = 'django_elasticsearch_dsl_drf.filter_backends.filtering.' \
'post_filter.common'
__author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
__copyright__ = '2017-2020 Artur Barseghyan'
__license__ = 'GPL 2.0/LGPL 2.1'
__all__ = ('PostFilterFilteringFilterBackend',)
[docs]class PostFilterFilteringFilterBackend(FilteringFilterBackend):
"""The ``post_filter`` filtering filter backend for Elasticsearch.
Example:
>>> from django_elasticsearch_dsl_drf.constants import (
>>> LOOKUP_FILTER_PREFIX,
>>> LOOKUP_FILTER_WILDCARD,
>>> LOOKUP_QUERY_EXCLUDE,
>>> LOOKUP_QUERY_ISNULL,
>>> )
>>> from django_elasticsearch_dsl_drf.filter_backends import (
>>> PostFilterFilteringFilterBackend
>>> )
>>> from django_elasticsearch_dsl_drf.viewsets import (
>>> BaseDocumentViewSet,
>>> )
>>>
>>> # Local article document definition
>>> from .documents import ArticleDocument
>>>
>>> # Local article document serializer
>>> from .serializers import ArticleDocumentSerializer
>>>
>>> class ArticleDocumentView(BaseDocumentViewSet):
>>>
>>> document = ArticleDocument
>>> serializer_class = ArticleDocumentSerializer
>>> filter_backends = [PostFilterFilteringFilterBackend,]
>>> post_filter_fields = {
>>> 'title': 'title.raw',
>>> 'state': {
>>> 'field': 'state.raw',
>>> 'lookups': [
>>> LOOKUP_FILTER_PREFIX,
>>> LOOKUP_FILTER_WILDCARD,
>>> LOOKUP_QUERY_EXCLUDE,
>>> LOOKUP_QUERY_ISNULL,
>>> ],
>>> }
>>> }
"""
[docs] @classmethod
def prepare_filter_fields(cls, view):
"""Prepare filter fields.
:param view:
:type view: rest_framework.viewsets.ReadOnlyModelViewSet
:return: Filtering options.
:rtype: dict
"""
filter_fields = view.post_filter_fields
for field, options in filter_fields.items():
if options is None or isinstance(options, string_types):
filter_fields[field] = {
'field': options or field
}
elif 'field' not in filter_fields[field]:
filter_fields[field]['field'] = field
if 'lookups' not in filter_fields[field]:
filter_fields[field]['lookups'] = tuple(
ALL_LOOKUP_FILTERS_AND_QUERIES
)
return filter_fields
[docs] @classmethod
def apply_filter(cls, queryset, options=None, args=None, kwargs=None):
"""Apply filter.
:param queryset:
:param options:
:param args:
:param kwargs:
:return:
"""
if args is None:
args = []
if kwargs is None:
kwargs = {}
return queryset.post_filter(*args, **kwargs)
[docs] @classmethod
def apply_query(cls, queryset, options=None, args=None, kwargs=None):
"""Apply query.
:param queryset:
:param options:
:param args:
:param kwargs:
:return:
"""
if args is None:
args = []
if kwargs is None:
kwargs = {}
return queryset.post_filter(*args, **kwargs)
[docs] def get_coreschema_field(self, field):
if isinstance(field, fields.IntegerField):
field_cls = coreschema.Number
else:
field_cls = coreschema.String
return field_cls()
[docs] def get_schema_fields(self, view):
assert coreapi is not None, 'coreapi must be installed to ' \
'use `get_schema_fields()`'
assert coreschema is not None, 'coreschema must be installed to ' \
'use `get_schema_fields()`'
filter_fields = getattr(view, 'post_filter_fields', None)
document = getattr(view, 'document', None)
return [] if not filter_fields else [
coreapi.Field(
name=field_name,
required=False,
location='query',
schema=self.get_coreschema_field(
document._fields.get(field_name)
)
)
for field_name in filter_fields
]