@ -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