Getting ES fields from a StringFilterES with a django_filters.filters.CharFilter

This commit is contained in:
Alejandro Nunez Capote 2019-05-31 20:57:44 -04:00
parent b0cba398a1
commit 4796d024d4
6 changed files with 128 additions and 0 deletions

View File

@ -0,0 +1,9 @@
import warnings
from ..utils import DJANGO_ELASTICSEARCH_DSL_INSTALLED
if not DJANGO_ELASTICSEARCH_DSL_INSTALLED:
warnings.warn(
"Use of elasticsearch integration requires the django_elasticsearch_dsl package "
"be installed. You can do so using `pip install django_elasticsearch_dsl`",
ImportWarning,
)

View File

@ -0,0 +1,9 @@
import warnings
from ...utils import DJANGO_FILTER_INSTALLED
if not DJANGO_FILTER_INSTALLED:
warnings.warn(
"Use of django elasticsearch filtering requires the django-filter package "
"be installed. You can do so using `pip install django-filter`",
ImportWarning,
)

View File

@ -0,0 +1,45 @@
"""Filters to ElasticSearch"""
from collections import OrderedDict
from django_filters import CharFilter
from elasticsearch_dsl import Q
class StringFilterES(object): # pylint: disable=R0902
"""String Fields specific to ElasticSearch."""
default_expr = 'contain'
filter_class = CharFilter
variants = {
"contain": lambda name, value: Q('match',
**{name: {
"query": value,
"fuzziness": "auto"
}}),
"term": lambda name, value: Q('term', **{name: value}),
}
def __init__(self, name=None, attr=None):
"""
:param name: Name of the field. This is the name that will be exported.
:param attr: Path to the index attr that will be used as filter.
"""
assert name or attr, "At least the field name or the field attr should be passed"
self.field_name = name or attr.replace('.', '_')
self.fields = self.generate_fields()
def generate_fields(self):
"""
All FilterSet objects should specify its fields for the introspection.
:return: A mapping of field to Filter type of field with all the suffix
expressions combinations.
"""
fields = OrderedDict()
for variant in self.variants:
variant_name = self.field_name if variant in ["default", self.default_expr] \
else "%s_%s" % (self.field_name, variant)
fields[variant_name] = self.filter_class(field_name=variant_name)
return fields

View File

@ -0,0 +1,55 @@
"""Fields"""
from collections import OrderedDict
from django.utils import six
from django_filters.filterset import BaseFilterSet
from .filters import StringFilterES
class FilterSetESMetaclass(type):
"""Captures the meta class of the filterSet class."""
def __new__(mcs, name, bases, attrs):
"""Get filters declared explicitly in the class"""
declared_filters = mcs.get_declared_filters(bases, attrs)
attrs['declared_filters'] = declared_filters
new_class = super(FilterSetESMetaclass, mcs).__new__(mcs, name, bases, attrs)
if issubclass(new_class, BaseFilterSet):
base_filters = OrderedDict()
for name, filter_field in six.iteritems(declared_filters):
base_filters.update(filter_field.fields)
new_class.base_filters = base_filters
return new_class
@classmethod
def get_declared_filters(mcs, bases, attrs):
"""
Get the filters declared in the class.
:param bases: base classes of the current class
:param attrs: attributes captured to be included as metadata
:return: An OrderedDict of filter fields declared in the class as static fields.
"""
# List of filters declared in the class as static fields.
filters = [
(filter_name, attrs.pop(filter_name))
for filter_name, obj in list(attrs.items())
if isinstance(obj, StringFilterES)
]
# Merge declared filters from base classes
for base in reversed(bases):
if hasattr(base, 'declared_filters'):
filters = [(name, field) for name, field in base.declared_filters.items() if name not in attrs] \
+ filters
return OrderedDict(filters)
class FilterSetES(six.with_metaclass(FilterSetESMetaclass, object)):
"""FilterSet specific for ElasticSearch."""
pass

View File

@ -1,5 +1,6 @@
from .utils import ( from .utils import (
DJANGO_FILTER_INSTALLED, DJANGO_FILTER_INSTALLED,
DJANGO_ELASTICSEARCH_DSL_INSTALLED,
get_reverse_fields, get_reverse_fields,
maybe_queryset, maybe_queryset,
get_model_fields, get_model_fields,
@ -10,6 +11,7 @@ from .testing import GraphQLTestCase
__all__ = [ __all__ = [
"DJANGO_FILTER_INSTALLED", "DJANGO_FILTER_INSTALLED",
"DJANGO_ELASTICSEARCH_DSL_INSTALLED",
"get_reverse_fields", "get_reverse_fields",
"maybe_queryset", "maybe_queryset",
"get_model_fields", "get_model_fields",

View File

@ -12,6 +12,14 @@ except ImportError:
DJANGO_FILTER_INSTALLED = False DJANGO_FILTER_INSTALLED = False
try:
import django_elasticsearch_dsl # noqa
DJANGO_ELASTICSEARCH_DSL_INSTALLED = True
except ImportError:
DJANGO_ELASTICSEARCH_DSL_INSTALLED = False
def get_reverse_fields(model, local_field_names): def get_reverse_fields(model, local_field_names):
for name, attr in model.__dict__.items(): for name, attr in model.__dict__.items():
# Don't duplicate any local fields # Don't duplicate any local fields