Skip to content

Commit c2e3871

Browse files
author
Nikolay Dolzhenkov
committed
Added more tests to test_filter_schema.py
1 parent 64ff476 commit c2e3871

File tree

2 files changed

+239
-37
lines changed

2 files changed

+239
-37
lines changed

ninja/filter_schema.py

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
# XOR is available only in Django 4.1+: https://docs.djangoproject.com/en/4.1/ref/models/querysets/#xor
1111
ExpressionConnector = Literal["AND", "OR", "XOR"]
1212

13-
DEFAULT_IGNORE_NONE = True
13+
DEFAULT_IGNORE_NONE: bool = True
1414
DEFAULT_CLASS_LEVEL_EXPRESSION_CONNECTOR: ExpressionConnector = "AND"
1515
DEFAULT_FIELD_LEVEL_EXPRESSION_CONNECTOR: ExpressionConnector = "OR"
1616

@@ -29,8 +29,8 @@ def __init__(
2929
self,
3030
q: str | List[str],
3131
*,
32-
expression_connector: str = DEFAULT_FIELD_LEVEL_EXPRESSION_CONNECTOR,
33-
ignore_none: Optional[bool] = DEFAULT_IGNORE_NONE,
32+
expression_connector: ExpressionConnector = DEFAULT_FIELD_LEVEL_EXPRESSION_CONNECTOR,
33+
ignore_none: bool = DEFAULT_IGNORE_NONE,
3434
):
3535
"""
3636
Args:
@@ -42,19 +42,14 @@ def __init__(
4242
ignore_none: Whether to ignore None values for this field specifically. Default is True.
4343
"""
4444
self.q = q
45-
self.expression_connector = cast(ExpressionConnector, expression_connector)
45+
self.expression_connector = expression_connector
4646
self.ignore_none = ignore_none
4747

4848

4949
T = TypeVar("T", bound=QuerySet)
5050

5151

5252
class FilterSchema(Schema):
53-
# if TYPE_CHECKING:
54-
# __config__: ClassVar[Type[FilterConfig]] = FilterConfig # pragma: no cover
55-
56-
# Config = FilterConfig
57-
5853
class Config(Schema.Config):
5954
ignore_none: bool = DEFAULT_IGNORE_NONE
6055
expression_connector: ExpressionConnector = (
@@ -97,9 +92,9 @@ def _get_filter_lookup(
9792
return filter_lookups[0]
9893
else:
9994
raise ImproperlyConfigured(
100-
f"Multiple FilterLookup instances found in metadata of {self.__class__.__name__}.{field_name}. "
101-
f"Use at most one FilterLookup instance per field. "
102-
f"If you need multiple lookups, specify them as a list in a single FilterLookup: "
95+
f"Multiple FilterLookup instances found in metadata of {self.__class__.__name__}.{field_name}.\n"
96+
f"Use at most one FilterLookup instance per field.\n"
97+
f"If you need multiple lookups, specify them as a list in a single FilterLookup:\n"
10398
f"{field_name}: Annotated[{field_info.annotation}, FilterLookup(['lookup1', 'lookup2', ...])]"
10499
)
105100

@@ -138,11 +133,7 @@ def _get_field_ignore_none(
138133
) -> bool | None:
139134
filter_lookup = self._get_filter_lookup(field_name, field_info)
140135
if filter_lookup:
141-
return (
142-
filter_lookup.ignore_none
143-
if filter_lookup.ignore_none is not None
144-
else default
145-
)
136+
return filter_lookup.ignore_none
146137

147138
# Legacy approach, consider removing in future versions
148139
field_extra = cast(dict, field_info.json_schema_extra) or {}
@@ -187,15 +178,18 @@ def _resolve_field_expression(
187178

188179
def _connect_fields(self) -> Q:
189180
q = Q()
181+
class_ignore_none = self.model_config.get("ignore_none", DEFAULT_IGNORE_NONE)
190182
for field_name, field_info in self.__class__.model_fields.items():
191183
filter_value = getattr(self, field_name)
192-
ignore_none = self._get_field_ignore_none(
193-
field_name,
194-
field_info,
195-
cast(
196-
bool | None,
197-
self.model_config.get("ignore_none", DEFAULT_IGNORE_NONE),
198-
),
184+
# class-level ignore_none set to False (non-default) takes precedence over field-level ignore_none
185+
ignore_none = (
186+
False
187+
if class_ignore_none is False
188+
else self._get_field_ignore_none(
189+
field_name,
190+
field_info,
191+
DEFAULT_IGNORE_NONE,
192+
)
199193
)
200194

201195
# Resolve Q expression for a field even if we skip it due to None value

0 commit comments

Comments
 (0)