11from enum import Enum
22from typing import Any , ClassVar
33
4- from models_library .utils .json_serialization import json_dumps
4+ from models_library .utils .json_serialization import json_dumps , json_loads
55from pydantic import BaseModel , Field , Json , validator
66
77from .basic_types import IDStr
@@ -17,7 +17,10 @@ class OrderBy(BaseModel):
1717 field : IDStr = Field (..., description = "field name identifier" )
1818 direction : OrderDirection = Field (
1919 default = OrderDirection .ASC ,
20- description = "As [A,B,C,...] if ASC or [Z,Y,X, ...] if DESC" ,
20+ description = (
21+ f"As [A,B,C,...] if `{ OrderDirection .ASC .value } `"
22+ f" or [Z,Y,X, ...] if `{ OrderDirection .DESC .value } `"
23+ ),
2124 )
2225
2326 class Config :
@@ -45,46 +48,61 @@ def create_order_by_query_model_classes(
4548
4649 assert default_order_by .field in sortable_fields # nosec
4750
48- field_options_msg = "|" .join (sorted (sortable_fields ))
49- direction_options_msg = "|" .join (sorted (OrderDirection ))
51+ msg_field_options = "|" .join (sorted (sortable_fields ))
52+ msg_direction_options = "|" .join (sorted (OrderDirection ))
53+ order_by_example : dict [str , Any ] = OrderBy .Config .schema_extra ["example" ]
5054
51- class _OrderBy (OrderBy ):
52- field : IDStr = Field (
53- ..., description = OrderBy .__fields__ ["field" ].field_info .description
54- )
55+ class _JsonOrderBy (OrderBy ):
5556 direction : OrderDirection = Field (
5657 default = default_order_by .direction
5758 if override_direction_default
5859 else OrderBy .__fields__ ["direction" ].default ,
5960 description = OrderBy .__fields__ ["direction" ].field_info .description ,
6061 )
6162
63+ @classmethod
64+ def __modify_schema__ (cls , field_schema : dict [str , Any ]) -> None :
65+ # openapi.json schema is corrected here
66+ field_schema .update (
67+ type = "string" ,
68+ format = "json-string" ,
69+ default = json_dumps (default_order_by ),
70+ example = json_dumps (order_by_example ),
71+ title = "Order By" ,
72+ )
73+
6274 @validator ("field" , allow_reuse = True )
6375 @classmethod
6476 def _check_if_sortable_field (cls , v ):
6577 if v not in sortable_fields :
6678 msg = (
6779 f"We do not support ordering by provided field '{ v } '. "
68- f"Fields supported are { field_options_msg } ."
80+ f"Fields supported are { msg_field_options } ."
6981 )
7082 raise ValueError (msg )
7183 return v
7284
7385 description = (
74- f"Order by field ({ field_options_msg } ) and direction ({ direction_options_msg } ). "
86+ f"Order by field ({ msg_field_options } ) and direction ({ msg_direction_options } ). "
7587 f"The default sorting order is '{ default_order_by .direction .value } ' on '{ default_order_by .field } '."
7688 )
77- example : dict [str , Any ] = OrderBy .Config .schema_extra ["example" ]
7889
79- class _OrderByQueryParams (BaseOrderByQueryParams ):
90+ class _RequestValidatorModel (BaseOrderByQueryParams ):
8091 # Used in rest handler for verification
81- order_by : _OrderBy = Field (
92+ order_by : _JsonOrderBy = Field (
8293 default = default_order_by ,
8394 description = description ,
84- example = example ,
8595 )
8696
87- class _OrderByQueryJsonParams (BaseModel ):
97+ @validator ("order_by" , allow_reuse = True , pre = True )
98+ @classmethod
99+ def _pre_parse_if_json (cls , v ):
100+ if isinstance (v , str ):
101+ # can raise a JsonEncoderError(TypeError)
102+ return json_loads (v )
103+ return v
104+
105+ class _OpenapiModel (BaseModel ):
88106 # Used to produce nice openapi.json specs
89107 order_by : Json = Field (
90108 default = json_dumps (default_order_by ),
@@ -95,7 +113,10 @@ class _OrderByQueryJsonParams(BaseModel):
95113 @classmethod
96114 def _validate_json_content (cls , v ):
97115 if v :
98- _OrderByQueryParams (order_by = v )
116+ _RequestValidatorModel (order_by = v )
99117 return v
100118
101- return _OrderByQueryParams , _OrderByQueryJsonParams
119+ class Config :
120+ schema_extra : ClassVar [dict [str , Any ]] = {"title" : "Order By Parameters" }
121+
122+ return _RequestValidatorModel , _OpenapiModel
0 commit comments