@@ -36,6 +36,7 @@ class OrderBy(BaseModel):
3636
3737
3838class _BaseOrderQueryParams (RequestParameters ):
39+ # Use OrderingQueryParams instead for more flexible ordering
3940 order_by : OrderBy
4041
4142
@@ -128,53 +129,65 @@ class _OrderJsonQueryParams(_BaseOrderQueryParams):
128129 return _OrderJsonQueryParams
129130
130131
132+ def _parse_order_by (v ):
133+ if not v :
134+ return []
135+
136+ if isinstance (v , list ):
137+ v = "," .join (v )
138+
139+ if not isinstance (v , str ):
140+ msg = "order_by must be a string"
141+ raise TypeError (msg )
142+
143+ # 1. from comma-separated string to list of OrderClause
144+ clauses = []
145+ for t in v .split ("," ):
146+ token = t .strip ()
147+ if not token :
148+ continue
149+ if token .startswith ("-" ):
150+ clauses .append ((token [1 :], OrderDirection .DESC ))
151+ elif token .startswith ("+" ):
152+ clauses .append ((token [1 :], OrderDirection .ASC ))
153+ else :
154+ clauses .append ((token , OrderDirection .ASC ))
155+
156+ # 2. check for duplicates and conflicting directions
157+ return [
158+ {"field" : field , "direction" : direction }
159+ for field , direction in check_ordering_list (clauses )
160+ ]
161+
162+
131163class OrderingQueryParams (GenericModel , Generic [TField ]):
132- # NOTE: OrderingQueryParams is a more flexible variant for generic usage and that
133- # does include multiple ordering clauses
134- #
164+ """
165+ This class is designed to parse query parameters for ordering results in an API request.
166+
167+ It supports multiple ordering clauses and allows for flexible sorting options.
168+
169+ NOTE: It only parses strings and validates into list[OrderClause[TField]]
170+ where TField is a type variable representing valid field names.
171+
172+
173+ For example:
174+
175+ /my/path?order_by=field1,-field2,+field3
176+
177+ would sort by field1 ascending, field2 descending, and field3 ascending.
178+ """
179+
135180 order_by : Annotated [
136181 list [OrderClause [TField ]],
137- Field (
138- default_factory = list ,
139- description = "Order by clauses e.g. ?order_by=-created_at,name" ,
140- ),
182+ BeforeValidator (_parse_order_by ),
183+ Field (default_factory = list ),
141184 ] = DEFAULT_FACTORY
142185
143- @field_validator ("order_by" , mode = "before" )
144- @classmethod
145- def _parse_order_by_string (cls , v ):
146- """Parses a comma-separated string into a list of OrderClause
147-
148- Example, given the query parameter `order_by` in a request like `GET /items?order_by=-created_at,name`
149- It parses to:
150- [
151- OrderClause(field="created_at", direction=OrderDirection.DESC),
152- OrderClause(field="name", direction=OrderDirection.ASC),
153- ]
154- """
155- if not v :
156- return []
157-
158- if isinstance (v , str ):
159- # 1. from comma-separated string to list of OrderClause
160- v = v .split ("," )
161- clauses : list [tuple [str , OrderDirection ]] = []
162- for t in v :
163- token = t .strip ()
164- if not token :
165- continue
166- if token .startswith ("-" ):
167- clauses .append ((token [1 :].strip (), OrderDirection .DESC ))
168- elif token .startswith ("+" ):
169- clauses .append ((token [1 :].strip (), OrderDirection .ASC ))
170- else :
171- clauses .append ((token , OrderDirection .ASC ))
172- # 2. check for duplicates and conflicting directions
173- return [
174- {"field" : field , "direction" : direction }
175- for field , direction in check_ordering_list (clauses )
176- ]
177-
178- # NOTE: Parses ONLY strings into list[OrderClause], otherwise raises TypeError
179- msg = f"Invalid type for order_by: expected str, got { type (v )} "
180- raise TypeError (msg )
186+ model_config = ConfigDict (
187+ json_schema_extra = {
188+ "examples" : [
189+ {"order_by" : "-created_at,name,+gender" },
190+ {"order_by" : "" },
191+ ],
192+ }
193+ )
0 commit comments