240 lines
7.7 KiB
Plaintext
240 lines
7.7 KiB
Plaintext
FilterSet Guide
|
|
===============
|
|
|
|
This document provides a guide on using additional FilterSet features.
|
|
|
|
Meta options
|
|
------------
|
|
|
|
- :ref:`model <model>`
|
|
- :ref:`fields <fields>`
|
|
- :ref:`exclude <exclude>`
|
|
- :ref:`order_by <order-by>`
|
|
- :ref:`form <form>`
|
|
- :ref:`together <together>`
|
|
|
|
|
|
.. _model:
|
|
|
|
Automatic filter generation with ``model``
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The ``FilterSet`` is capable of automatically generating filters for a given
|
|
``model``'s fields. Similar to Django's ``ModelForm``, filters are created
|
|
based on the underlying model field's type. This option must be combined with
|
|
either the ``fields`` or ``exclude`` option, which is the same requirement for
|
|
Django's ``ModelForm`` class, detailed `here`__.
|
|
|
|
__ https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#selecting-the-fields-to-use
|
|
|
|
.. code-block:: python
|
|
|
|
class UserFilter(django_filters.FilterSet):
|
|
class Meta:
|
|
model = User
|
|
fields = ['username', 'last_login']
|
|
|
|
|
|
.. _fields:
|
|
|
|
Declaring filterable ``fields``
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The ``fields`` option is combined with ``model`` to automatically generate
|
|
filters. Note that generated filters will not overwrite filters declared on
|
|
the ``FilterSet``. The ``fields`` option accepts two syntaxes:
|
|
|
|
* a list of field names
|
|
* a dictionary of field names mapped to a list of lookups
|
|
|
|
.. code-block:: python
|
|
|
|
class UserFilter(django_filters.FilterSet):
|
|
class Meta:
|
|
model = User
|
|
fields = ['username', 'last_login']
|
|
|
|
# or
|
|
|
|
class UserFilter(django_filters.FilterSet):
|
|
class Meta:
|
|
model = User
|
|
fields = {
|
|
'username': ['exact', 'contains'],
|
|
'last_login': ['exact', 'year__gt'],
|
|
}
|
|
|
|
The list syntax will create an ``exact`` lookup filter for each field included
|
|
in ``fields``. The dictionary syntax will create a filter for each lookup
|
|
expression declared for its corresponding model field. These expressions may
|
|
include both transforms and lookups, as detailed in the `lookup reference`__.
|
|
|
|
__ https://docs.djangoproject.com/en/dev/ref/models/lookups/#module-django.db.models.lookups
|
|
|
|
|
|
.. _exclude:
|
|
|
|
Disable filter fields with ``exclude``
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The ``exclude`` option accepts a blacklist of field names to exclude from
|
|
automatic filter generation. Note that this option will not disable filters
|
|
declared directly on the ``FilterSet``.
|
|
|
|
.. code-block:: python
|
|
|
|
class UserFilter(django_filters.FilterSet):
|
|
class Meta:
|
|
model = User
|
|
exclude = ['password']
|
|
|
|
|
|
.. _order-by:
|
|
|
|
Ordering using ``order_by``
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
You can allow the user to control ordering by providing the
|
|
``order_by`` argument in the Filter's Meta class. ``order_by`` can be either a
|
|
``list`` or ``tuple`` of field names, in which case those are the options, or
|
|
it can be a ``bool`` which, if True, indicates that all fields that
|
|
the user can filter on can also be sorted on. An example of ordering using a list::
|
|
|
|
import django_filters
|
|
|
|
class ProductFilter(django_filters.FilterSet):
|
|
price = django_filters.NumberFilter(lookup_expr='lt')
|
|
class Meta:
|
|
model = Product
|
|
fields = ['price', 'release_date']
|
|
order_by = ['price']
|
|
|
|
If you want to control the display of items in ``order_by``, you can set it to
|
|
a list or tuple of 2-tuples in the format ``(field_name, display_name)``.
|
|
This lets you override the displayed names for your ordering fields::
|
|
|
|
order_by = (
|
|
('name', 'Company Name'),
|
|
('average_rating', 'Stars'),
|
|
)
|
|
|
|
Note that the default query parameter name used for ordering is ``o``. You
|
|
can override this by setting an ``order_by_field`` attribute on the
|
|
``FilterSet`` class to the string value you would like to use.
|
|
|
|
|
|
.. _form:
|
|
|
|
Custom Forms using ``form``
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The inner ``Meta`` class also takes an optional ``form`` argument. This is a
|
|
form class from which ``FilterSet.form`` will subclass. This works similar to
|
|
the ``form`` option on a ``ModelAdmin.``
|
|
|
|
|
|
.. _together:
|
|
|
|
Group fields with ``together``
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
The inner ``Meta`` class also takes an optional ``together`` argument. This
|
|
is a list of lists, each containing field names. For convenience can be a
|
|
single list/tuple when dealing with a single set of fields. Fields within a
|
|
field set must either be all or none present in the request for
|
|
``FilterSet.form`` to be valid::
|
|
|
|
import django_filters
|
|
|
|
class ProductFilter(django_filters.FilterSet):
|
|
class Meta:
|
|
model = Product
|
|
fields = ['price', 'release_date', 'rating']
|
|
together = ['rating', 'price']
|
|
|
|
|
|
Non-Meta options
|
|
----------------
|
|
|
|
Note that these options do not go in the Meta class, they are specified directly
|
|
in your FilterSet class.
|
|
|
|
- filter_overrides
|
|
- order_by_field
|
|
- :ref:`strict <strict>`
|
|
|
|
|
|
.. _strict:
|
|
|
|
``strict``
|
|
~~~~~~~~~~
|
|
|
|
The ``strict`` option controls whether results are returned when an invalid
|
|
value is specified by the user for any filter field. By default, ``strict`` is
|
|
set to ``STRICTNESS.RETURN_NO_RESULTS`` meaning that an empty queryset is
|
|
returned if any field contains an invalid value. You can loosen this behavior
|
|
by setting ``strict`` to ``STRICTNESS.IGNORE`` which will effectively ignore a
|
|
filter field if its value is invalid. A third option of
|
|
``STRICTNESS.RAISE_VALIDATION_ERROR`` will cause a ``ValidationError`` to be
|
|
raised if any field contains an invalid value.
|
|
|
|
Overriding ``FilterSet`` methods
|
|
--------------------------------
|
|
|
|
``filter_for_lookup()``
|
|
~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Prior to version 0.13.0, filter generation did not take into account the
|
|
``lookup_expr`` used. This commonly caused malformed filters to be generated
|
|
for 'isnull', 'in', and 'range' lookups (as well as transformed lookups). The
|
|
current implementation provides the following behavior:
|
|
|
|
- 'isnull' lookups return a ``BooleanFilter``
|
|
- 'in' lookups return a filter derived from the CSV-based ``BaseInFilter``.
|
|
- 'range' lookups return a filter derived from the CSV-based ``BaseRangeFilter``.
|
|
|
|
If you want to override the ``filter_class`` and ``params`` used to instantiate
|
|
filters for a model field, you can override ``filter_for_lookup()``. Ex::
|
|
|
|
class ProductFilter(django_filters.FilterSet):
|
|
class Meta:
|
|
model = Product
|
|
fields = {
|
|
'release_date': ['exact', 'range'],
|
|
}
|
|
|
|
@classmethod
|
|
def filter_for_lookup(cls, f, lookup_type):
|
|
# override date range lookups
|
|
if isinstance(f, models.DateField) and lookup_type == 'range':
|
|
return django_filters.DateRangeFiler, {}
|
|
|
|
# use default behavior otherwise
|
|
return super(ProductFilter, cls).filter_for_lookup(f, lookup_type)
|
|
|
|
|
|
``get_ordering_field()``
|
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
If you want to use a custom widget, or in any other way override the ordering
|
|
field you can override the ``get_ordering_field()`` method on a ``FilterSet``.
|
|
This method just needs to return a Form Field.
|
|
|
|
Ordering on multiple fields, or other complex orderings can be achieved by
|
|
overriding the ``FilterSet.get_order_by()`` method. This is passed the selected
|
|
``order_by`` value, and is expected to return an iterable of values to pass to
|
|
``QuerySet.order_by``. For example, to sort a ``User`` table by last name, then
|
|
first name::
|
|
|
|
class UserFilter(django_filters.FilterSet):
|
|
class Meta:
|
|
order_by = (
|
|
('username', 'Username'),
|
|
('last_name', 'Last Name')
|
|
)
|
|
|
|
def get_order_by(self, order_value):
|
|
if order_value == 'last_name':
|
|
return ['last_name', 'first_name']
|
|
return super(UserFilter, self).get_order_by(order_value)
|