1+ import warnings
12from typing import Any , List , Optional , TypeVar , Union , cast
23
34from django .core .exceptions import ImproperlyConfigured
67from pydantic .fields import FieldInfo
78from typing_extensions import Literal
89
10+ from .constants import NOT_SET
911from .schema import Schema
1012
1113# XOR is available only in Django 4.1+: https://docs.djangoproject.com/en/4.1/ref/models/querysets/#xor
@@ -28,7 +30,7 @@ class MyFilterSchema(FilterSchema):
2830
2931 def __init__ (
3032 self ,
31- q : Union [str , List [str ]],
33+ q : Union [str , List [str ], None ],
3234 * ,
3335 expression_connector : ExpressionConnector = DEFAULT_FIELD_LEVEL_EXPRESSION_CONNECTOR ,
3436 ignore_none : bool = DEFAULT_IGNORE_NONE ,
@@ -107,43 +109,48 @@ def _get_field_q_expression(
107109 self ,
108110 field_name : str ,
109111 field_info : FieldInfo ,
110- default : Union [str , List [str ], None ] = None ,
111112 ) -> Union [str , List [str ], None ]:
112113 filter_lookup = self ._get_filter_lookup (field_name , field_info )
113114 if filter_lookup :
114- return filter_lookup .q if filter_lookup . q is not None else default
115+ return filter_lookup .q
115116
116117 # Legacy approach, consider removing in future versions
117- field_extra = cast (dict , field_info .json_schema_extra ) or {}
118- return cast (Union [str , List [str ], None ], field_extra .get ("q" , default ))
118+ return cast (
119+ Union [str , List [str ], None ],
120+ self ._get_from_deprecated_field_extra (field_name , field_info , "q" ),
121+ )
119122
120123 def _get_field_expression_connector (
121124 self ,
122125 field_name : str ,
123126 field_info : FieldInfo ,
124- default : Union [ExpressionConnector , None ] = None ,
125127 ) -> Union [ExpressionConnector , None ]:
126128 filter_lookup = self ._get_filter_lookup (field_name , field_info )
127129 if filter_lookup :
128- return filter_lookup .expression_connector or default
130+ return filter_lookup .expression_connector
129131
130132 # Legacy approach, consider removing in future versions
131- field_extra = cast (dict , field_info .json_schema_extra ) or {}
132133 return cast (
133- Union [ExpressionConnector , None ],
134- field_extra .get ("expression_connector" , default ),
134+ Union [ExpressionConnector | None ],
135+ self ._get_from_deprecated_field_extra (
136+ field_name , field_info , "expression_connector"
137+ ),
135138 )
136139
137140 def _get_field_ignore_none (
138- self , field_name : str , field_info : FieldInfo , default : Union [ bool , None ] = None
141+ self , field_name : str , field_info : FieldInfo
139142 ) -> Union [bool , None ]:
140143 filter_lookup = self ._get_filter_lookup (field_name , field_info )
141144 if filter_lookup :
142145 return filter_lookup .ignore_none
143146
144147 # Legacy approach, consider removing in future versions
145- field_extra = cast (dict , field_info .json_schema_extra ) or {}
146- return cast (Union [bool , None ], field_extra .get ("ignore_none" , default ))
148+ return cast (
149+ Union [bool , None ],
150+ self ._get_from_deprecated_field_extra (
151+ field_name , field_info , "ignore_none"
152+ ),
153+ )
147154
148155 def _resolve_field_expression (
149156 self , field_name : str , field_value : Any , field_info : FieldInfo
@@ -153,8 +160,9 @@ def _resolve_field_expression(
153160 return cast (Q , func (field_value ))
154161
155162 q_expression = self ._get_field_q_expression (field_name , field_info )
156- expression_connector = self ._get_field_expression_connector (
157- field_name , field_info , default = DEFAULT_FIELD_LEVEL_EXPRESSION_CONNECTOR
163+ expression_connector = (
164+ self ._get_field_expression_connector (field_name , field_info )
165+ or DEFAULT_FIELD_LEVEL_EXPRESSION_CONNECTOR
158166 )
159167
160168 if not q_expression :
@@ -191,11 +199,14 @@ def _connect_fields(self) -> Q:
191199 ignore_none = (
192200 False
193201 if class_ignore_none is False
194- else self ._get_field_ignore_none (
195- field_name ,
196- field_info ,
197- DEFAULT_IGNORE_NONE ,
202+ else field_ignore_none
203+ if (
204+ field_ignore_none := self ._get_field_ignore_none (
205+ field_name , field_info
206+ )
198207 )
208+ is not None
209+ else DEFAULT_IGNORE_NONE
199210 )
200211
201212 # Resolve Q expression for a field even if we skip it due to None value
@@ -213,3 +224,27 @@ def _connect_fields(self) -> Q:
213224 )
214225
215226 return q
227+
228+ def _get_from_deprecated_field_extra (
229+ self , field_name : str , field_info : FieldInfo , attr : str
230+ ) -> Union [Any , None ]:
231+ """
232+ Backward-compatible shim which looks up filtering parameters in the Field's **extra kwargs.
233+ Consider removing this method in favor of FilterLookup annotation class.
234+ """
235+ field_extra = cast (dict , field_info .json_schema_extra ) or {}
236+ value = field_extra .get (attr , NOT_SET )
237+
238+ if value is not NOT_SET :
239+ warnings .warn (
240+ f"Using Pydantic Field with extra keyword arguments ('{ attr } ') "
241+ f"in field { self .__class__ .__name__ } .{ field_name } is deprecated. Please use ninja.FilterLookup instead:\n "
242+ f" from typing import Annotated\n "
243+ f" from ninja import FilterLookup, FilterSchema\n \n "
244+ f" class { self .__class__ .__name__ } (FilterSchema):\n "
245+ f" { field_name } : Annotated[Optional[...], FilterLookup(q='...', ...)] = None" ,
246+ DeprecationWarning ,
247+ stacklevel = 4 ,
248+ )
249+ return value
250+ return None
0 commit comments