Filtering🔗
In this section, we'll cover the everything necessary for adding filtering
for your QueryTypes
.
FilterSet🔗
A FilterSet
is a collection of Filter
objects that can be applied
to a QueryType
. In GraphQL, they represent an InputObjectType
, which when
added to a QueryType
creates an input argument for filtering the
results of a QueryType
.
A basic FilterSet
is created by subclassing FilterSet
and adding its Django Model as a generic type parameter:
Auto-generation🔗
By default, a FilterSet
automatically introspects its model and converts the model's fields
to input fields on the generated InputObjectType
. Given the following models:
Simply subclassing FilterSet
creates an InputObjectType
which has all of the Task
model's
fields and those fields' lookups translated into input arguments, as well as the logical operators
NOT
, AND
, OR
, XOR
, allowing users to freely create any filtering conditions they want.
The actual InputObjectType
is omitted here for brevity, since it has quite many fields.
Let's give a few examples on how to use it. Assuming we added the TaskFilterSet
to a QueryType
named TaskType
, which in turn has been added to the GraphQL schema
as a list Entrypoint
tasks
, we can filter to only tasks that are done:
To see done tasks in a certain project:
To see all tasks that either start with "a" or end with "a":
About Filter
names
Usually the names of the Filters
generated by auto-generation correspond to the lookup
in Django, but for text-based fields, names are changed slightly to lean towards using
case-insensitive lookups first: Filter name
uses __iexact
and nameExact
uses __exact
.
Similarly, nameStartsWith
uses __istartswith
while nameStartsWithExact
uses __startswith
, etc.
You can disable auto-generation by setting the auto
argument to False
in the class definition:
Alternatively, you could exclude some Filters
from the auto-generation by setting the exclude
argument:
You can exclude either a model field (created_at
) or a specific
lookup on that model field (created_at_gte
, not created_at__gte
).
Filter queryset🔗
Together with its Filters
, a FilterSet
also provides a __filter_queryset__
classmethod. This method can be used to add filtering that should always be applied
when fetching objects through QueryTypes
using this FilterSet
.
As QueryTypes
also have a __filter_queryset__
classmethod, its important to note
the order in which these are applied by the Optimizer.
Schema name🔗
By default, the name of the generated InputObjectType
is the same as the name of the FilterSet
class.
If you want to change the name, you can do so by setting the schema_name
argument:
Description🔗
You can provide a description using the description
argument.
Directives🔗
You can add directives to the FilterSet
by providing them using the directives
argument.
See the Directives section for more details on directives.
GraphQL extensions🔗
You can provide custom extensions for the FilterSet
by providing a
extensions
argument with a dictionary containing them. These can then be used
however you wish to extend the functionality of the FilterSet
.
FilterSet
extensions are made available in the GraphQL InputObjectType
extensions
after the schema is created. The FilterSet
itself is found in the extensions
under a key defined by the FILTERSET_EXTENSIONS_KEY
setting.
Filter🔗
A Filter
is a class that is used to define a possible filter input for a FilterSet
.
Usually Filters
correspond to fields on the Django model for their respective FilterSet
.
In GraphQL, it represents an GraphQLInputField
in an InputObjectType
.
A Filter
always requires a reference from which it will create the proper GraphQL resolver,
input type for the Filter
.
Model field references🔗
As seen in the FilterSet
section, you don't need to provide model fields
explicitly thanks to auto-generation, but if you wanted to be more explicit,
you could add the Filters
to the FilterSet
class body. In this case, the Filter
can be used
without a reference, as its attribute name in the FilterSet
class body can be used to identify
the corresponding model field.
To be a bit more explicit, you could use a string referencing the model field:
For better type safety, you can also use the model field itself:
Being explicit like this is only required if the name of the attribute in the GraphQL schema is different from the model field name.
Expression references🔗
Django ORM expressions can also be used as the references.
These create an alias in the queryset when the Filter
is used.
Remember that subqueries are also counted as expressions.
Function references🔗
Functions (or methods) can also be used to create Filters
.
This can be done by decorating a method with the Filter
class.
The Filter
method should return a Q
expression.
The type of the value
argument is used as the input type for the Filter
, so typing it is required.
About method signature
The decorated method is treated as a static method by the Filter
.
The self
argument is not an instance of the FilterSet
,
but the instance of the Filter
that is being used.
The info
argument can be left out, but if it's included, it should always
have the GQLInfo
type annotation.
The value
argument is the value provided for the filter. It should always be named "value",
and is required to be a keyword only argument.
Lookup🔗
By default, when defining a Filter
on a FilterSet
, the "exact" lookup expression
is used. This can be changed by providing the lookup
argument to the Filter
.
Note that auto-generation adds Filters
all possible combinations
of lookups for a model field, so you don't need to add them manually.
Many🔗
The many
argument changes the behavior of the Filter
such that it takes
a list of values instead of a single value. Then, each of the values are combined
as defined by the match
argument to form a single filter condition.
This would create the following filter input (ignoring auto-generation):
So if a query is filtered using this filter with the value ["foo", "bar"]
,
the filter condition would be Q(name__icontains="foo") | Q(name__icontains="bar")
.
Match🔗
The match
changes the behavior of the many
argument to combine the
provided values with a different operation. The default is any
, which means
that the filter condition will include an item if it matches any of the provided values.
The match
argument can be set to all
if all of the values should match,
or one_of
if only one of the values should match.
Distinct🔗
If using some Filter
would require a call to queryset.distinct()
(e.g. lookups spanning "to-many" relations), you can use the distinct
argument
to tell the FilterSet
for the Filter
to do that if the Filter
is used in a query.
Required🔗
By default, all Filter
are not required (nullable in GraphQL terms).
If you want to make a Filter
required, you can do so by setting the required
argument to True
.
Required aliases🔗
The required_aliases
argument can be used to specify additional expressions that
should be added as aliases to the queryset when the Filter
is used. This is useful
as a way to make the actual Filter
more readable when complex expressions are needed.
Field name🔗
A field_name
can be provided to explicitly set the Django model field name
that the Filter
corresponds to. This can be useful when the field has a different
name and type in the GraphQL schema than in the model.
Schema name🔗
A schema_name
can be provided to override the name of the Filter
in the GraphQL schema.
This can be useful for renaming fields for the schema, or when the desired name is a Python keyword
and cannot be used as the Filter
attribute name.
Description🔗
By default, a Filter
is able to determine its description based on its reference.
For example, for a model field, the description is taken from its help_text
.
If the reference has no description, or you wish to add a different one, this can be done in two ways:
1) By setting the description
argument.
2) As class variable docstrings.
When using function references, instead of a class variable docstring, you add a docstring to the function/method used as the reference instead.
Deprecation reason🔗
A deprecation_reason
can be provided to mark the Filter
as deprecated.
This is for documentation purposes only, and does not affect the use of the Filter
.
Empty filter result🔗
A special EmptyFilterResult
exception can be raised from a Filter
to indicate
that the usage of the Filter
should result in an empty queryset, e.g. because
of the value given or for permission reasons.
Raising this exceptions skips the rest of the Filter
logic and results in an empty queryset.
Permissions🔗
You can add permissions check to individual Filters
by using a custom function
and adding the permission check inline.
You can also raise the EmptyFilterResult
exception if the usage of the filter
should result in an empty queryset instead of an error.
Directives🔗
You can add directives to the Filter
by providing them using the directives
argument.
See the Directives section for more details on directives.
GraphQL extensions🔗
You can provide custom extensions for the Filter
by providing a
extensions
argument with a dictionary containing them. These can then be used
however you wish to extend the functionality of the Filter
.
Filter
extensions are made available in the GraphQL InputField
extensions
after the schema is created. The Filter
itself is found in the extensions
under a key defined by the FILTER_EXTENSIONS_KEY
setting.