11from __future__ import annotations
22
3+ import functools
34import typing
45
56from core .const .tag import OpenAPITag
67from core .serializer .json_schema_serializer import JsonSchemaSerializer
8+ from django .db .models .base import Model
79from django .db .models .fields .files import FileField
8- from django .db .models .fields .related import ForeignKey
10+ from django .db .models .fields .related import ForeignKey , ManyToManyField
911from drf_spectacular import openapi , types , utils
1012from modeltranslation .fields import TranslationField
1113from rest_framework import decorators , response , status , viewsets
@@ -18,6 +20,23 @@ def __new__(cls, *args: tuple, **kwargs: dict) -> JsonSchemaViewSet:
1820
1921 return super ().__new__ (cls )
2022
23+ @staticmethod
24+ @functools .lru_cache
25+ def get_enum_values (model : Model , is_nullable : bool ) -> list [dict [str , str ]]:
26+ enum_values : list [dict [str , str ]] = [{"const" : None , "title" : "빈 값" }] if is_nullable else []
27+
28+ if hasattr (model , "objects" ):
29+ qs = model .objects .all ()
30+ if hasattr (qs , "filter_active" ):
31+ qs = qs .filter_active ()
32+ elif hasattr (model , "is_active" ):
33+ qs = qs .filter (is_active = True )
34+
35+ for row in list (qs ):
36+ enum_values .append ({"const" : str (row .pk ), "title" : str (row )})
37+
38+ return enum_values
39+
2140 def get_json_schema (self ) -> dict :
2241 serializer_class = typing .cast (type [JsonSchemaSerializer ], self .get_serializer_class ())
2342
@@ -27,38 +46,22 @@ def get_json_schema(self) -> dict:
2746 "translation_fields" : set (),
2847 }
2948
30- nullable_fields = [
31- k for k , v in serializer_class .get_json_schema ()["properties" ].items () if "null" in v .get ("type" , [])
32- ]
33-
3449 if hasattr (serializer_class .Meta , "model" ) and "properties" in result ["schema" ]:
3550 model_fields = serializer_class .Meta .model ._meta .fields
51+ model_m2m_fields = serializer_class .Meta .model ._meta .many_to_many
3652
37- for field in model_fields :
38- if isinstance (field , ForeignKey ):
39- enum_values = []
40- row_qs = field .related_model .objects
41- if hasattr (row_qs , "filter_active" ):
42- row_qs = row_qs .filter_active ()
43- elif hasattr (field .related_model , "is_active" ):
44- row_qs = row_qs .filter (is_active = True )
45-
46- for row in row_qs :
47- enum_values .append ({"id" : row .pk , "name" : str (row )})
48-
49- if field .name in result ["schema" ]["properties" ]:
50- result ["schema" ]["properties" ][field .name ]["enum" ] = [e ["id" ] for e in enum_values ] + (
51- [None ] if field .null else []
52- )
53-
54- result ["ui_schema" ][field .name ] = {
55- "ui:options" : {
56- "ui:widget" : "select" ,
57- "enumNames" : [f"{ e ['name' ]} <{ e ['id' ]} >" for e in enum_values ]
58- + (["빈 값" ] if field .name in nullable_fields else []),
59- }
60- }
53+ for field in model_fields + model_m2m_fields :
54+ if field .name not in result ["schema" ]["properties" ]:
55+ continue
6156
57+ if isinstance (field , ForeignKey ):
58+ e_values = self .get_enum_values (field .related_model , field .null )
59+ result ["schema" ]["properties" ][field .name ]["oneOf" ] = e_values
60+ elif isinstance (field , ManyToManyField ):
61+ e_values = self .get_enum_values (field .related_model , False )
62+ result ["schema" ]["properties" ][field .name ]["items" ]["oneOf" ] = e_values
63+ result ["schema" ]["properties" ][field .name ]["uniqueItems" ] = True
64+ result ["ui_schema" ][field .name ] = {"ui:field" : "m2m_select" }
6265 elif isinstance (field , FileField ):
6366 result ["ui_schema" ][field .name ] = {"ui:field" : "file" }
6467 elif isinstance (field , TranslationField ):
0 commit comments