Browse Source

feat: impl filtro m2m com lookup '__in' para buscas com multiplos ids

pull/3807/head
LeandroJatai 1 week ago
parent
commit
1dd4568e28
  1. 61
      drfautoapi/drfautoapi.py

61
drfautoapi/drfautoapi.py

@ -1,20 +1,21 @@
from collections import OrderedDict from collections import OrderedDict
import django_filters
import importlib import importlib
import inspect import inspect
import logging import logging
import re import re
from django.apps.config import AppConfig
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 import TextField, CharField
from django.db.models.fields.files import FileField from django.db.models.fields.files import FileField
from django.db.models.fields.related import ManyToManyField
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 from django.urls.conf import path
from django_filters.constants import ALL_FIELDS, EMPTY_VALUES from django_filters.constants import ALL_FIELDS, EMPTY_VALUES
from django_filters.fields import ModelMultipleChoiceField
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
@ -23,6 +24,7 @@ from rest_framework import serializers as rest_serializers
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
from rest_framework.urlpatterns import format_suffix_patterns
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -65,6 +67,21 @@ class SplitStringCharFilter(django_filters.CharFilter):
return qs return qs
class M2MFilter(django_filters.ModelMultipleChoiceFilter):
class M2MFieldFormField(ModelMultipleChoiceField):
def clean(self, value):
if value in EMPTY_VALUES:
return super().clean(value)
values = list(map(lambda x: x.replace(' ', '').split(','), value))
values = [v for subvalues in values for v in subvalues]
cleaned_values = tuple(super().clean(values))
return (cleaned_values,) if cleaned_values else cleaned_values
field_class = M2MFieldFormField
distinct = True
class ApiFilterSetMixin(FilterSet): class ApiFilterSetMixin(FilterSet):
o = CharFilter(method='filter_o') o = CharFilter(method='filter_o')
@ -117,7 +134,7 @@ class ApiFilterSetMixin(FilterSet):
f = model._meta.get_field(f_str) f = model._meta.get_field(f_str)
if f.many_to_many: if f.many_to_many:
fields[f_str] = ['exact'] fields[f_str] = ['exact', 'in', ]
continue continue
fields[f_str] = ['exact'] fields[f_str] = ['exact']
@ -173,6 +190,14 @@ class ApiFilterSetMixin(FilterSet):
return filter_class(**default) return filter_class(**default)
return None return None
@classmethod
def filter_for_lookup(cls, field, lookup_type):
f, p = super().filter_for_lookup(field, lookup_type)
if lookup_type == 'in' and isinstance(field, ManyToManyField):
return M2MFilter, p
return f, p
class BusinessRulesNotImplementedMixin: class BusinessRulesNotImplementedMixin:
http_method_names = ['get', 'head', 'options', 'trace'] http_method_names = ['get', 'head', 'options', 'trace']
@ -187,6 +212,26 @@ class BusinessRulesNotImplementedMixin:
raise Exception(_("DELETE Delete não implementado")) raise Exception(_("DELETE Delete não implementado"))
class DrfautoapiRouter(DefaultRouter):
def get_urls(self):
"""
Generate the list of URL patterns, including a default root view
for the API, and appending `.json` style format suffixes.
"""
urls = super(DefaultRouter, self).get_urls()
if self.include_root_view:
view = self.get_api_root_view(api_urls = urls)
root_url = path('', view, name = self.root_view_name)
urls.append(root_url)
if self.include_format_suffixes:
urls = format_suffix_patterns(urls, allowed = ['[a-zA-Z.0-9]+'])
return urls
class ApiViewSetConstrutor(): class ApiViewSetConstrutor():
_built_sets = {} _built_sets = {}
@ -208,12 +253,14 @@ class ApiViewSetConstrutor():
importlib.import_module(m) importlib.import_module(m)
@classmethod @classmethod
def router(cls, router_class=DefaultRouter): def router(cls, router_class = DrfautoapiRouter):
router = router_class() router = router_class()
for app, built_sets in cls._built_sets.items(): for app, built_sets in cls._built_sets.items():
for model, viewset in built_sets.items(): for model, viewset in built_sets.items():
router.register( router.register(
f'{app.label}/{model._meta.model_name}', viewset) f'{app.label}/{model._meta.model_name}',
viewset,
basename=f'{app.label}_{model._meta.model_name}')
return router return router
@classmethod @classmethod
@ -407,3 +454,5 @@ class customize(object):
ApiViewSetConstrutor._built_sets[ ApiViewSetConstrutor._built_sets[
self.model._meta.app_config][self.model] = _ApiViewSet self.model._meta.app_config][self.model] = _ApiViewSet
return _ApiViewSet return _ApiViewSet

Loading…
Cancel
Save