Skip to content

Commit 20224bc

Browse files
author
Nikolay Dolzhenkov
committed
Updated filtering.md to align with newly supported annotation
1 parent 00a727e commit 20224bc

File tree

2 files changed

+56
-23
lines changed

2 files changed

+56
-23
lines changed

docs/docs/guides/input/filtering.md

Lines changed: 55 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -72,26 +72,52 @@ class BookFilterSchema(FilterSchema):
7272
```
7373
The `name` field will be converted into `Q(name=...)` expression.
7474

75-
When your database lookups are more complicated than that, you can explicitly specify them in the field definition using a `"q"` kwarg:
76-
```python hl_lines="2"
75+
When your database lookups are more complicated than that, you can annotate your fields with an instance of `FilterLookup` where you specify how you wish your field to be looked up for filtering:
76+
```python hl_lines="5"
77+
from ninja import FilterSchema, FilterLookup
78+
from typing import Annotated
79+
7780
class BookFilterSchema(FilterSchema):
78-
name: Optional[str] = Field(None, q='name__icontains')
81+
name: Annotated[Optional[str], FilterLookup("name__icontains")] = None
7982
```
80-
You can even specify multiple lookup keyword argument names as a list:
81-
```python hl_lines="2 3 4"
83+
84+
You can even specify multiple lookups as a list:
85+
```python hl_lines="3 4 5"
8286
class BookFilterSchema(FilterSchema):
83-
search: Optional[str] = Field(None, q=['name__icontains',
84-
'author__name__icontains',
85-
'publisher__name__icontains'])
87+
search: Annotated[Optional[str], FilterLookup(
88+
["name__icontains",
89+
"author__name__icontains",
90+
"publisher__name__icontains"]
91+
)]
8692
```
93+
94+
By default, field-level expressions are combined using `"OR"` connector, so with the above setup, a query parameter `?search=foobar` will search for books that have "foobar" in either of their name, author or publisher.
95+
8796
And to make generic fields, you can make the field name implicit by skipping it:
88-
```python hl_lines="2"
89-
IContainsField = Annotated[Optional[str], Field(None, q='__icontains')]
97+
```python hl_lines="1 4"
98+
IContainsField = Annotated[Optional[str], FilterLookup('__icontains')]
9099

91100
class BookFilterSchema(FilterSchema):
92-
name: IContainsField
101+
name: IContainsField = None
93102
```
94-
By default, field-level expressions are combined using `"OR"` connector, so with the above setup, a query parameter `?search=foobar` will search for books that have "foobar" in either of their name, author or publisher.
103+
104+
??? note "Deprecated syntax"
105+
106+
In previous versions, database lookups were specified using `Field(q=...)` syntax:
107+
```python
108+
from ninja import FilterSchema, Field
109+
110+
class BookFilterSchema(FilterSchema):
111+
name: Optional[str] = Field(None, q="name__icontains")
112+
```
113+
114+
This approach is still supported, but it is considered **deprecated** and **not recommended** for new code because:
115+
116+
- Poor IDE support (IDEs don't recognize custom `Field` arguments)
117+
- Uses deprecated Pydantic features (`**extra`)
118+
- Less type-safe and harder to maintain
119+
120+
The new `FilterLookup` annotation provides better developer experience with full IDE support and type safety. Prefer using `FilterLookup` for new projects.
95121

96122

97123
## Combining expressions
@@ -103,7 +129,9 @@ By default,
103129
So, with the following `FilterSchema`...
104130
```python
105131
class BookFilterSchema(FilterSchema):
106-
search: Optional[str] = Field(None, q=['name__icontains', 'author__name__icontains'])
132+
search: Annotated[
133+
Optional[str],
134+
FilterLookup(["name__icontains", "author__name__icontains"])] = None
107135
popular: Optional[bool] = None
108136
```
109137
...and the following query parameters from the user
@@ -114,14 +142,18 @@ the `FilterSchema` instance will look for popular books that have `harry` in the
114142

115143

116144
You can customize this behavior using an `expression_connector` argument in field-level and class-level definition:
117-
```python hl_lines="3 7"
145+
```python hl_lines="6 11"
118146
class BookFilterSchema(FilterSchema):
119-
active: Optional[bool] = Field(None, q=['is_active', 'publisher__is_active'],
120-
expression_connector='AND')
121-
name: Optional[str] = Field(None, q='name__icontains')
147+
active: Annotated[
148+
Optional[bool],
149+
FilterLookup(
150+
["is_active", "publisher__is_active"],
151+
expression_connector="AND"
152+
)] = None
153+
name: Annotated[Optional[str], FilterLookup("name__icontains")] = None
122154

123155
class Config:
124-
expression_connector = 'OR'
156+
expression_connector = "OR"
125157
```
126158

127159
An expression connector can take the values of `"OR"`, `"AND"` and `"XOR"`, but the latter is only [supported](https://docs.djangoproject.com/en/4.1/ref/models/querysets/#xor) in Django starting with 4.1.
@@ -139,17 +171,17 @@ You can make the `FilterSchema` treat `None` as a valid value that should be fil
139171
This can be done on a field level with a `ignore_none` kwarg:
140172
```python hl_lines="3"
141173
class BookFilterSchema(FilterSchema):
142-
name: Optional[str] = Field(None, q='name__icontains')
143-
tag: Optional[str] = Field(None, q='tag', ignore_none=False)
174+
name: Annotated[Optional[str], FilterLookup("name__icontains")] = None
175+
tag: Annotated[Optional[str], FilterLookup("tag", ignore_none=False)] = None
144176
```
145177

146178
This way when no other value for `"tag"` is provided by the user, the filtering will always include a condition `tag=None`.
147179

148-
You can also specify this settings for all fields at the same time in the Config:
180+
You can also specify this setting for all fields at the same time in the Config:
149181
```python hl_lines="6"
150182
class BookFilterSchema(FilterSchema):
151-
name: Optional[str] = Field(None, q='name__icontains')
152-
tag: Optional[str] = Field(None, q='tag', ignore_none=False)
183+
name: Annotated[Optional[str], FilterLookup("name__icontains")] = None
184+
tag: Optional[str] = None
153185

154186
class Config:
155187
ignore_none = False

docs/mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ markdown_extensions:
9999
- abbr
100100
- codehilite
101101
- admonition
102+
- pymdownx.details
102103
- pymdownx.superfences
103104
plugins:
104105
- search

0 commit comments

Comments
 (0)