Filtering
It is possible to define filters for Django types, which will
be converted into .filter(...)
queries for the ORM:
Tip
In most cases filter fields should have Optional
annotations and default value strawberry.UNSET
like so:
foo: Optional[SomeType] = strawberry.UNSET
Above auto
annotation is wrapped in Optional
automatically.
UNSET
is automatically used for fields without field
or with strawberry_django.filter_field
.
The code above would generate following schema:
Tip
If you are using the relay integration and working with types inheriting
from relay.Node
and GlobalID
for identifying objects, you might want to set
MAP_AUTO_ID_AS_GLOBAL_ID=True
in your strawberry django settings
to make sure auto
fields gets mapped to GlobalID
on types and filters.
AND, OR, NOT, DISTINCT …
To every filter AND
, OR
, NOT
& DISTINCT
fields are added to allow more complex filtering
Lookups
Lookups can be added to all fields with lookups=True
, which will
add more options to resolve each type. For example:
The code above would generate the following schema:
Single-field lookup can be annotated with the FilterLookup
generic type.
Filtering over relationships
The code above would generate following schema:
Custom filter methods
You can define custom filter method by defining your own resolver.
Warning
It is discouraged to use queryset.filter()
directly. When using more
complex filtering via NOT
, OR
& AND
this might lead to undesired behaviour.
[!TIP]
process_filters
As seen above
strawberry_django.process_filters
function is exposed and can be reused in custom methods. Above it’s used to resolve fields lookupsnull values
By default
null
value is ignored for all filters & lookups. This applies to custom filter methods as well. Those won’t even be called (you don’t have to check forNone
). This can be modified usingstrawberry_django.filter_field(filter_none=True)
This also means that built in
exact
&iExact
lookups cannot be used to filter forNone
andisNull
have to be used explicitly.value resolution
value
parameter of typerelay.GlobalID
is resolved to itsnode_id
attributevalue
parameter of typeEnum
is resolved to is’s value- above types are converted in
lists
as wellresolution can modified via
strawberry_django.filter_field(resolve_value=...)
- True - always resolve
- False - never resolve
- UNSET (default) - resolves for filters without custom method only
The code above generates the following schema:
Resolver arguments
-
prefix
- represents the current path or position- Required
- Important for nested filtering
- In code bellow custom filter
name
ends up filteringFruit
instead ofColor
without applyingprefix
-
value
- represents graphql field type- Required, but forbidden for default
filter
method - must be annotated
- used instead of field’s return type
- Required, but forbidden for default
-
queryset
- can be used for more complex filtering- Optional, but Required for default
filter
method - usually used to
annotate
QuerySet
- Optional, but Required for default
Resolver return
For custom field methods two return values are supported
- django’s
Q
object - tuple with
QuerySet
and django’sQ
object ->tuple[QuerySet, Q]
For default filter
method only second variant is supported.
What about nulls?
By default null
values are ignored. This can be toggled as such @strawberry_django.filter_field(filter_none=True)
Overriding the default filter
method
Works similar to field filter method, but:
- is responsible for resolution of filtering for entire object
- must be named
filter
- argument
queryset
is Required - argument
value
is Forbidden
Tip
As seen above strawberry_django.process_filters
function is exposed and can be
reused in custom methods.
For filter method filter
skip_object_order_method
was used to avoid endless recursion.
Adding filters to types
All fields and CUD mutations inherit filters from the underlying type by default. So, if you have a field like this:
The fruits
field will inherit the filters
of the type in the same way as
if it was passed to the field.
Adding filters directly into a field
Filters added into a field override the default filters of this type.
Generic Lookup reference
There is 7 already defined Generic Lookup strawberry.input
classes importable from strawberry_django
BaseFilterLookup
- contains
exact
,isNull
&inList
- used for
ID
&bool
fields
RangeLookup
- used for
range
orBETWEEN
filtering
ComparisonFilterLookup
- inherits
BaseFilterLookup
- additionaly contains
gt
,gte
,lt
,lte
, &range
- used for Numberical fields
FilterLookup
- inherits
BaseFilterLookup
- additionally contains
iExact
,contains
,iContains
,startsWith
,iStartsWith
,endsWith
,iEndsWith
,regex
&iRegex
- used for string based fields and as default
DateFilterLookup
- inherits
ComparisonFilterLookup
- additionally contains
year
,month
,day
,weekDay
,isoWeekDay
,week
,isoYear
&quarter
- used for date based fields
TimeFilterLookup
- inherits
ComparisonFilterLookup
- additionally contains
hour
,minute
,second
,date
&time
- used for time based fields
DatetimeFilterLookup
- inherits
DateFilterLookup
&TimeFilterLookup
- used for timedate based fields
Legacy filtering
The previous version of filters can be enabled via USE_DEPRECATED_FILTERS
Warning
If USE_DEPRECATED_FILTERS is not set to True
legacy custom filtering
methods will be not be called.
When using legacy filters it is important to use legacy
strawberry_django.filters.FilterLookup
lookups as well.
The correct version is applied for auto
annotated filter field (given lookups=True
being set). Mixing old and new lookups
might lead to error DuplicatedTypeName: Type StrFilterLookup is defined multiple times in the schema
.
While legacy filtering is enabled new filtering custom methods are
fully functional including default filter
method.
Migration process could be composed of these steps:
- enable USE_DEPRECATED_FILTERS
- gradually transform custom filter field methods to new version (do not forget to use old FilterLookup if applicable)
- gradually transform default
filter
methods - disable USE_DEPRECATED_FILTERS - This is breaking change