|
@ -2,17 +2,19 @@ from collections import OrderedDict |
|
|
import importlib |
|
|
import importlib |
|
|
import inspect |
|
|
import inspect |
|
|
import logging |
|
|
import logging |
|
|
|
|
|
import re |
|
|
|
|
|
|
|
|
from django.apps.config import AppConfig |
|
|
from django.apps.config import AppConfig |
|
|
from django.apps.registry import apps |
|
|
from django.apps.registry import apps |
|
|
from django.conf import settings |
|
|
from django.conf import settings |
|
|
from django.contrib.postgres.fields.jsonb import JSONField |
|
|
from django.contrib.postgres.fields.jsonb import JSONField |
|
|
from django.db.models.base import ModelBase |
|
|
from django.db.models.base import ModelBase |
|
|
|
|
|
from django.db.models.fields import TextField, CharField |
|
|
from django.db.models.fields.files import FileField |
|
|
from django.db.models.fields.files import FileField |
|
|
from django.template.defaultfilters import capfirst |
|
|
from django.template.defaultfilters import capfirst |
|
|
from django.utils.translation import ugettext_lazy as _ |
|
|
from django.utils.translation import ugettext_lazy as _ |
|
|
import django_filters |
|
|
import django_filters |
|
|
from django_filters.constants import ALL_FIELDS |
|
|
from django_filters.constants import ALL_FIELDS, EMPTY_VALUES |
|
|
from django_filters.filters import CharFilter |
|
|
from django_filters.filters import CharFilter |
|
|
from django_filters.filterset import FilterSet |
|
|
from django_filters.filterset import FilterSet |
|
|
from django_filters.rest_framework.backends import DjangoFilterBackend |
|
|
from django_filters.rest_framework.backends import DjangoFilterBackend |
|
@ -26,6 +28,43 @@ from rest_framework.viewsets import ModelViewSet |
|
|
logger = logging.getLogger(__name__) |
|
|
logger = logging.getLogger(__name__) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class SplitStringCharFilter(django_filters.CharFilter): |
|
|
|
|
|
_re = re.compile(r'("[^"]+"| +|[^"]+)') |
|
|
|
|
|
|
|
|
|
|
|
def filter(self, qs, value): |
|
|
|
|
|
if value in EMPTY_VALUES: |
|
|
|
|
|
return qs |
|
|
|
|
|
if self.distinct: |
|
|
|
|
|
qs = qs.distinct() |
|
|
|
|
|
lookup = '%s__%s' % (self.field_name, self.lookup_expr) |
|
|
|
|
|
|
|
|
|
|
|
values = [value] |
|
|
|
|
|
if self.lookup_expr == 'icontains': |
|
|
|
|
|
if not '"' in value: |
|
|
|
|
|
values = value.split(' ') |
|
|
|
|
|
else: |
|
|
|
|
|
values = list( |
|
|
|
|
|
filter( |
|
|
|
|
|
lambda x: x and x != ' ' and x[0] != '"', |
|
|
|
|
|
self._re.findall(value) |
|
|
|
|
|
) |
|
|
|
|
|
) + list( |
|
|
|
|
|
map( |
|
|
|
|
|
lambda x: x[1:-1], |
|
|
|
|
|
filter( |
|
|
|
|
|
lambda x: x and x[0] == '"', |
|
|
|
|
|
self._re.findall(value) |
|
|
|
|
|
) |
|
|
|
|
|
) |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
if not isinstance(values, list): |
|
|
|
|
|
values = [values] |
|
|
|
|
|
for v in values: |
|
|
|
|
|
qs = self.get_method(qs)(**{lookup: v}) |
|
|
|
|
|
return qs |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ApiFilterSetMixin(FilterSet): |
|
|
class ApiFilterSetMixin(FilterSet): |
|
|
|
|
|
|
|
|
o = CharFilter(method='filter_o') |
|
|
o = CharFilter(method='filter_o') |
|
@ -39,6 +78,12 @@ class ApiFilterSetMixin(FilterSet): |
|
|
'lookup_expr': 'exact', |
|
|
'lookup_expr': 'exact', |
|
|
}, |
|
|
}, |
|
|
}, |
|
|
}, |
|
|
|
|
|
CharField: { |
|
|
|
|
|
'filter_class': SplitStringCharFilter, |
|
|
|
|
|
}, |
|
|
|
|
|
TextField: { |
|
|
|
|
|
'filter_class': SplitStringCharFilter, |
|
|
|
|
|
}, |
|
|
JSONField: { |
|
|
JSONField: { |
|
|
'filter_class': django_filters.CharFilter, |
|
|
'filter_class': django_filters.CharFilter, |
|
|
'extra': lambda f: { |
|
|
'extra': lambda f: { |
|
@ -81,16 +126,16 @@ class ApiFilterSetMixin(FilterSet): |
|
|
r = [] |
|
|
r = [] |
|
|
for lk, lv in cl.items(): |
|
|
for lk, lv in cl.items(): |
|
|
|
|
|
|
|
|
if lk == 'contained_by': |
|
|
if lk in ('contained_by', 'trigram_similar', 'unaccent', 'search'): |
|
|
continue |
|
|
continue |
|
|
|
|
|
|
|
|
sflk = f'{sub_f}{"__" if sub_f else ""}{lk}' |
|
|
sflk = f'{sub_f}{"__" if sub_f else ""}{lk}' |
|
|
r.append(sflk) |
|
|
r.append(sflk) |
|
|
|
|
|
|
|
|
if hasattr(lv, 'class_lookups'): |
|
|
if hasattr(lv, 'get_lookups'): |
|
|
r += get_keys_lookups(lv.class_lookups, sflk) |
|
|
r += get_keys_lookups(lv.get_lookups(), sflk) |
|
|
|
|
|
|
|
|
if hasattr(lv, 'output_field') and hasattr(lv, 'output_field.class_lookups'): |
|
|
if hasattr(lv, 'output_field') and hasattr(lv, 'output_field.get_lookups'): |
|
|
r.append(f'{sflk}{"__" if sflk else ""}range') |
|
|
r.append(f'{sflk}{"__" if sflk else ""}range') |
|
|
|
|
|
|
|
|
r += get_keys_lookups(lv.output_field.class_lookups, sflk) |
|
|
r += get_keys_lookups(lv.output_field.class_lookups, sflk) |
|
@ -98,7 +143,7 @@ class ApiFilterSetMixin(FilterSet): |
|
|
return r |
|
|
return r |
|
|
|
|
|
|
|
|
fields[f_str] = list( |
|
|
fields[f_str] = list( |
|
|
set(fields[f_str] + get_keys_lookups(f.class_lookups, ''))) |
|
|
set(fields[f_str] + get_keys_lookups(f.get_lookups(), ''))) |
|
|
|
|
|
|
|
|
# Remove excluded fields |
|
|
# Remove excluded fields |
|
|
exclude = exclude or [] |
|
|
exclude = exclude or [] |
|
|