debian-django-filter/docs/guide/tips.txt

91 lines
3.4 KiB
Plaintext

==================
Tips and Solutions
==================
Common problems for declared filters
------------------------------------
Below are some of the common problem that occur when declaring filters. It is
recommended that you read this as it provides a more complete understanding of
how filters work.
Filter ``name`` and ``lookup_expr`` not configured
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
While ``name`` and ``lookup_expr`` are optional, it is recommended that you specify
them. By default, if ``name`` is not specified, the filter's name on the
filterset class will be used. Additionally, ``lookup_expr`` defaults to
``exact``. The following is an example of a misconfigured price filter:
.. code-block:: python
class ProductFilter(django_filters.FilterSet):
price__gt = django_filters.NumberFilter()
The filter instance will have a field name of ``price__gt`` and an ``exact``
lookup type. Under the hood, this will incorrectly be resolved as:
.. code-block:: python
Produce.objects.filter(price__gt__exact=value)
The above will most likely generate a ``FieldError``. The correct configuration
would be:
.. code-block:: python
class ProductFilter(django_filters.FilterSet):
price__gt = django_filters.NumberFilter(name='price', lookup_expr='gt')
Missing ``lookup_expr`` for text search filters
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
It's quite common to forget to set the lookup expression for :code:`CharField`
and :code:`TextField` and wonder why a search for "foo" does not return results
for "foobar". This is because the default lookup type is ``exact``, but you
probably want to perform an ``icontains`` lookup.
Filter and lookup expression mismatch (in, range, isnull)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
It's not always appropriate to directly match a filter to its model field's
type, as some lookups expect different types of values. This is a commonly
found issue with ``in``, ``range``, and ``isnull`` lookups. Let's look
at the following product model:
.. code-block:: python
class Product(models.Model):
category = models.ForeignKey(Category, null=True)
Given that ``category`` is optional, it's reasonable to want to enable a search
for uncategorized products. The following is an incorrectly configured
``isnull`` filter:
.. code-block:: python
class ProductFilter(django_filters.FilterSet):
uncategorized = django_filters.NumberFilter(name='category', lookup_expr='isnull')
So what's the issue? While the underlying column type for ``category`` is an
integer, ``isnull`` lookups expect a boolean value. A ``NumberFilter`` however
only validates numbers. Filters are not `'expression aware'` and won't change
behavior based on their ``lookup_expr``. You should use filters that match the
data type of the lookup expression `instead` of the data type underlying the
model field. The following would correctly allow you to search for both
uncategorized products and products for a set of categories:
.. code-block:: python
class NumberInFilter(django_filters.BaseInFilter, django_filters.NumberFilter):
pass
class ProductFilter(django_filters.FilterSet):
categories = NumberInFilter(name='category', lookup_expr='in')
uncategorized = django_filters.BooleanFilter(name='category', lookup_expr='isnull')
More info on constructing ``in`` and ``range`` csv :ref:`filters <base-in-filter>`.