From c43f1d011cef742c782750883df73eae2c25bd7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?LeandroJata=C3=AD?= Date: Wed, 22 Oct 2025 12:40:10 -0300 Subject: [PATCH] feat: impl filtro m2m com lookup '__in' para buscas com multiplos ids (#3807) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: impl filtro m2m com lookup '__in' para buscas com multiplos ids * fix: remove alteração na criação de rotas da api A classe DrfautoapiRouter removida nesse commit bem como a alteração no classmethod router é uma aleração necessária para que a api rode no django 5.2. Por outro lado, esta alteração quebra os links gerados pela template tag url e exigiria refatoração das mesmas. Esta alteração não é necessária para o propósito do PR. --- drfautoapi/drfautoapi.py | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/drfautoapi/drfautoapi.py b/drfautoapi/drfautoapi.py index e0fd0c348..da5a3a736 100644 --- a/drfautoapi/drfautoapi.py +++ b/drfautoapi/drfautoapi.py @@ -1,20 +1,21 @@ from collections import OrderedDict +import django_filters import importlib import inspect import logging import re -from django.apps.config import AppConfig -from django.apps.registry import apps from django.conf import settings from django.contrib.postgres.fields.jsonb import JSONField 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.related import ManyToManyField from django.template.defaultfilters import capfirst 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.fields import ModelMultipleChoiceField from django_filters.filters import CharFilter from django_filters.filterset import FilterSet 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.routers import DefaultRouter from rest_framework.viewsets import ModelViewSet +from rest_framework.urlpatterns import format_suffix_patterns logger = logging.getLogger(__name__) @@ -65,6 +67,21 @@ class SplitStringCharFilter(django_filters.CharFilter): 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): o = CharFilter(method='filter_o') @@ -117,7 +134,7 @@ class ApiFilterSetMixin(FilterSet): f = model._meta.get_field(f_str) if f.many_to_many: - fields[f_str] = ['exact'] + fields[f_str] = ['exact', 'in', ] continue fields[f_str] = ['exact'] @@ -173,6 +190,14 @@ class ApiFilterSetMixin(FilterSet): return filter_class(**default) 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: http_method_names = ['get', 'head', 'options', 'trace'] @@ -208,7 +233,7 @@ class ApiViewSetConstrutor(): importlib.import_module(m) @classmethod - def router(cls, router_class=DefaultRouter): + def router(cls, router_class = DefaultRouter): router = router_class() for app, built_sets in cls._built_sets.items(): for model, viewset in built_sets.items(): @@ -407,3 +432,5 @@ class customize(object): ApiViewSetConstrutor._built_sets[ self.model._meta.app_config][self.model] = _ApiViewSet return _ApiViewSet + +