Browse Source

Refatora api para incluir filtro via lookups passados por queryparams (#3445)

* isola filtro mixin para adição de lookups

* refatora api para filtrar com lookups
pull/3446/head
Leandro Roberto Silva 3 years ago
committed by GitHub
parent
commit
9ae1f401ae
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      sapl/api/core/__init__.py
  2. 105
      sapl/api/core/filters.py
  3. 61
      sapl/api/forms.py

8
sapl/api/core/__init__.py

@ -20,7 +20,7 @@ from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from sapl.api.forms import SaplFilterSetMixin from sapl.api.core.filters import SaplFilterSetMixin
from sapl.api.permissions import SaplModelPermissions from sapl.api.permissions import SaplModelPermissions
from sapl.api.serializers import ChoiceSerializer, ParlamentarSerializer,\ from sapl.api.serializers import ChoiceSerializer, ParlamentarSerializer,\
ParlamentarEditSerializer, ParlamentarResumeSerializer ParlamentarEditSerializer, ParlamentarResumeSerializer
@ -56,6 +56,7 @@ class SaplApiViewSetConstrutor():
# Carrega todas as classes de sapl.api.serializers que possuam # Carrega todas as classes de sapl.api.serializers que possuam
# "Serializer" como Sufixo. # "Serializer" como Sufixo.
serializers_classes = inspect.getmembers(serializers) serializers_classes = inspect.getmembers(serializers)
serializers_classes = {i[0]: i[1] for i in filter( serializers_classes = {i[0]: i[1] for i in filter(
lambda x: x[0].endswith('Serializer'), lambda x: x[0].endswith('Serializer'),
serializers_classes serializers_classes
@ -82,7 +83,8 @@ class SaplApiViewSetConstrutor():
serializer_name, rest_serializers.ModelSerializer) serializer_name, rest_serializers.ModelSerializer)
# Caso Exista, pega a classe sapl.api.forms.{model}FilterSet # Caso Exista, pega a classe sapl.api.forms.{model}FilterSet
# ou utiliza a base definida em sapl.forms.SaplFilterSetMixin # ou utiliza a base definida em
# sapl.api.core.filters.SaplFilterSetMixin
filter_name = f'{object_name}FilterSet' filter_name = f'{object_name}FilterSet'
_filterset_class = filters_classes.get( _filterset_class = filters_classes.get(
filter_name, SaplFilterSetMixin) filter_name, SaplFilterSetMixin)
@ -127,7 +129,7 @@ class SaplApiViewSetConstrutor():
class Meta(_meta_filterset): class Meta(_meta_filterset):
if not hasattr(_meta_filterset, 'model'): if not hasattr(_meta_filterset, 'model'):
model = _model model = _model
# Define uma classe padrão ModelViewSet de DRF # Define uma classe padrão ModelViewSet de DRF
class ModelSaplViewSet(SaplApiViewSetConstrutor.SaplApiViewSet): class ModelSaplViewSet(SaplApiViewSetConstrutor.SaplApiViewSet):
queryset = _model.objects.all() queryset = _model.objects.all()

105
sapl/api/core/filters.py

@ -0,0 +1,105 @@
from collections import OrderedDict
from django.conf import settings
from django.db.models.fields import DateTimeField, DateField
from django.db.models.fields.files import FileField
from django.template.defaultfilters import capfirst
import django_filters
from django_filters.constants import ALL_FIELDS
from django_filters.filters import CharFilter
from django_filters.filterset import BaseFilterSet, FilterSetMetaclass, \
FilterSet
from django_filters.utils import resolve_field, get_all_model_fields
class SaplFilterSetMixin(FilterSet):
o = CharFilter(method='filter_o')
class Meta:
fields = '__all__'
filter_overrides = {
FileField: {
'filter_class': django_filters.CharFilter,
'extra': lambda f: {
'lookup_expr': 'exact',
},
},
}
def filter_o(self, queryset, name, value):
try:
return queryset.order_by(
*map(str.strip, value.split(',')))
except:
return queryset
@classmethod
def get_fields(cls):
model = cls._meta.model
fields_model = get_all_model_fields(model)
fields_filter = cls._meta.fields
exclude = cls._meta.exclude
if exclude is not None and fields_filter is None:
fields_filter = ALL_FIELDS
fields = fields_filter if isinstance(fields_filter, dict) else {}
if not isinstance(fields_filter, (dict, str)):
for f in fields_filter:
fields[f] = ['exact']
for f_str in fields_model:
if f_str not in fields:
f = model._meta.get_field(f_str)
fields[f_str] = []
def get_keys_lookups(cl, sub_f):
r = []
for lk, lv in cl.items():
if lk == 'contained_by':
continue
sflk = f'{sub_f}{"__" if sub_f else ""}{lk}'
r.append(sflk)
if hasattr(lv, 'class_lookups'):
r += get_keys_lookups(lv.class_lookups, sflk)
if hasattr(lv, 'output_field'):
r.append(f'{sflk}{"__" if sflk else ""}range')
r += get_keys_lookups(lv.output_field.class_lookups, sflk)
return r
fields[f_str] = list(
set(get_keys_lookups(f.class_lookups, '')))
# Remove excluded fields
exclude = exclude or []
fields = [(f, lookups)
for f, lookups in fields.items() if f not in exclude]
return OrderedDict(fields)
@classmethod
def filter_for_field(cls, f, name, lookup_expr='exact'):
# Redefine método estático para ignorar filtro para
# fields que não possuam lookup_expr informado
f, lookup_type = resolve_field(f, lookup_expr)
default = {
'field_name': name,
'label': capfirst(f.verbose_name),
'lookup_expr': lookup_expr
}
filter_class, params = cls.filter_for_lookup(
f, lookup_type)
default.update(params)
if filter_class is not None:
return filter_class(**default)
return None

61
sapl/api/forms.py

@ -1,65 +1,8 @@
from django.db.models.fields.files import FileField
from django.template.defaultfilters import capfirst
import django_filters
from django_filters.filters import CharFilter, NumberFilter
from django_filters.rest_framework.filterset import FilterSet
from django_filters.utils import resolve_field
from sapl.sessao.models import SessaoPlenaria
class SaplFilterSetMixin(FilterSet):
o = CharFilter(method='filter_o')
class Meta:
fields = '__all__'
filter_overrides = {
FileField: {
'filter_class': django_filters.CharFilter,
'extra': lambda f: {
'lookup_expr': 'exact',
},
},
}
def filter_o(self, queryset, name, value):
try:
return queryset.order_by(
*map(str.strip, value.split(',')))
except:
return queryset
@classmethod from sapl.api.core.filters import SaplFilterSetMixin
def filter_for_field(cls, f, name, lookup_expr='exact'): from sapl.sessao.models import SessaoPlenaria
# Redefine método estático para ignorar filtro para
# fields que não possuam lookup_expr informado
f, lookup_type = resolve_field(f, lookup_expr)
default = {
'field_name': name,
'label': capfirst(f.verbose_name),
'lookup_expr': lookup_expr
}
filter_class, params = cls.filter_for_lookup(
f, lookup_type)
default.update(params)
if filter_class is not None:
return filter_class(**default)
return None
class SessaoPlenariaFilterSet(SaplFilterSetMixin): class SessaoPlenariaFilterSet(SaplFilterSetMixin):
year = NumberFilter(method='filter_year')
month = NumberFilter(method='filter_month')
class Meta(SaplFilterSetMixin.Meta): class Meta(SaplFilterSetMixin.Meta):
model = SessaoPlenaria model = SessaoPlenaria
def filter_year(self, queryset, name, value):
qs = queryset.filter(data_inicio__year=value)
return qs
def filter_month(self, queryset, name, value):
qs = queryset.filter(data_inicio__month=value)
return qs

Loading…
Cancel
Save