diff --git a/api/views.py b/api/views.py
index 7ca19523..f17d525a 100644
--- a/api/views.py
+++ b/api/views.py
@@ -6,6 +6,8 @@
from rest_framework.response import Response
from api.serializers import DatasetDetailSerializer, DatasetSerializer, GenericSerializer
+from core.filters import parse_querystring
+from core.forms import get_table_dynamic_form
from core.models import Dataset, Table
from core.templatetags.utils import obfuscate
@@ -26,6 +28,11 @@ def retrieve(self, request, slug):
return Response(serializer.data)
+class InvalidFiltersException(Exception):
+ def __init__(self, errors_list):
+ self.errors_list = errors_list
+
+
class DatasetDataListView(ListAPIView):
pagination_class = paginators.LargeTablePageNumberPagination
@@ -44,9 +51,16 @@ def get_queryset(self):
del querystring[pagination_key]
Model = self.get_model_class()
- queryset = Model.objects.filter_by_querystring(querystring)
+ query, search_query, order_by = parse_querystring(querystring)
- return queryset
+ DynamicForm = get_table_dynamic_form(self.get_table())
+ filter_form = DynamicForm(data=query)
+ if filter_form.is_valid():
+ query = {k: v for k, v in filter_form.cleaned_data.items() if v != ""}
+ else:
+ raise InvalidFiltersException(filter_form.errors)
+
+ return Model.objects.composed_query(query, search_query, order_by)
def get_serializer_class(self):
table = self.get_table()
@@ -72,6 +86,12 @@ def get_serializer(self, *args, **kwargs):
return super().get_serializer(*args, **kwargs)
+ def handle_exception(self, exc):
+ if isinstance(exc, InvalidFiltersException):
+ return Response(exc.errors_list, status=400)
+ else:
+ return super().handle_exception(exc)
+
dataset_list = DatasetViewSet.as_view({"get": "list"})
dataset_detail = DatasetViewSet.as_view({"get": "retrieve"}, lookup_field="slug")
diff --git a/core/filters.py b/core/filters.py
index d76787a0..300af14b 100644
--- a/core/filters.py
+++ b/core/filters.py
@@ -8,6 +8,15 @@ def clean_value(key, value):
return key, value
+def parse_querystring(querystring):
+ query = querystring.copy()
+ order_by = query.pop("order-by", [""])
+ order_by = [field.strip().lower() for field in order_by[0].split(",") if field.strip()]
+ search_query = query.pop("search", [""])[0]
+ query = {key: value for key, value in query.items() if value}
+ return query, search_query, order_by
+
+
class DynamicModelFilterProcessor:
def __init__(self, filtering: dict, allowed_filters: list):
self.filtering = filtering
diff --git a/core/forms.py b/core/forms.py
index 98b7c0a6..58be27df 100644
--- a/core/forms.py
+++ b/core/forms.py
@@ -104,3 +104,20 @@ class ContactForm(forms.Form):
class DatasetSearchForm(forms.Form):
search = forms.CharField(label="Titulo ou Descrição")
+
+
+def get_table_dynamic_form(table, cache=True):
+ def config_dynamic_filter(model_field):
+ dynamic_field = table.get_field(model_field.name)
+ kwargs = {"required": False, "label": dynamic_field.title}
+ field_factory = model_field.formfield
+
+ if dynamic_field.has_choices:
+ kwargs["choices"] = [("", "Todos")] + [(c, c) for c in dynamic_field.choices["data"]]
+ field_factory = forms.ChoiceField
+
+ return field_factory(**kwargs)
+
+ model = table.get_model(cache=cache)
+ fields = model.extra["filtering"]
+ return forms.modelform_factory(model, fields=fields, formfield_callback=config_dynamic_filter)
diff --git a/core/models.py b/core/models.py
index 631002b6..72a91d80 100644
--- a/core/models.py
+++ b/core/models.py
@@ -113,18 +113,6 @@ def apply_ordering(self, query):
return qs
- def filter_by_querystring(self, querystring):
- query, search_query, order_by = self.parse_querystring(querystring)
- return self.composed_query(query, search_query, order_by)
-
- def parse_querystring(self, querystring):
- query = querystring.copy()
- order_by = query.pop("order-by", [""])
- order_by = [field.strip().lower() for field in order_by[0].split(",") if field.strip()]
- search_query = query.pop("search", [""])[0]
- query = {key: value for key, value in query.items() if value}
- return query, search_query, order_by
-
def composed_query(self, filter_query=None, search_query=None, order_by=None):
qs = self
if search_query:
@@ -377,6 +365,9 @@ def get_dynamic_model_mixins(self):
custom_mixins = [] if not self.dynamic_table_config else self.dynamic_table_config.get_model_mixins()
return custom_mixins + mixins
+ def get_field(self, name):
+ return self.fields.get(name=name)
+
def get_model(self, cache=True, data_table=None):
# TODO: the current dynamic model registry is handled by Brasil.IO's
# code but it needs to be delegated to dynamic_models.
@@ -423,6 +414,7 @@ def get_model(self, cache=True, data_table=None):
"filtering": filtering,
"ordering": ordering,
"search": search,
+ "table": self,
}
DYNAMIC_MODEL_REGISTRY[cache_key] = Model
return Model
diff --git a/core/templates/core/dataset-detail.html b/core/templates/core/dataset-detail.html
index 1fa1d733..ac505027 100644
--- a/core/templates/core/dataset-detail.html
+++ b/core/templates/core/dataset-detail.html
@@ -82,23 +82,15 @@
Filtros
- {% for field in fields %}
- {% if field.frontend_filter %}{% with value=query_dict|getplainattribute:field|default:'' %}
+ {% for field in filter_form %}