Merge pull request #402 from rpkilby/common-issues

Add common problems section, restructure docs usage/filter guide
This commit is contained in:
Carlton Gibson 2016-04-08 23:40:48 +02:00
commit 7a59a2ab85
3 changed files with 82 additions and 9 deletions

View File

@ -240,3 +240,15 @@ texinfo_documents = [
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
# see:
# https://github.com/snide/sphinx_rtd_theme#using-this-theme-locally-then-building-on-read-the-docs
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
# only import and set the theme if we're building docs locally
if not on_rtd:
import sphinx_rtd_theme
html_theme = 'sphinx_rtd_theme'
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]

View File

@ -460,10 +460,15 @@ database. So if in the DB for the given field you have values of 5, 7, and 9
each of those is present as an option. This is similar to the default behavior
of the admin.
.. _base-in-filter:
``BaseInFilter``
~~~~~~~~~~~~~~~~
This is a base class used for generating IN lookup filters. Values are expected to be comma separated.
This is a base class used for creating IN lookup filters. It is expected that
this filter class is used in conjunction with another filter class, as this
class **only** validates that the incoming value is comma-separated. The secondary
filter is then used to validate the individual values.
Example. ::
@ -488,7 +493,9 @@ Example. ::
``BaseRangeFilter``
~~~~~~~~~~~~~~~~~~~
This is a base class used for generating RANGE lookup filters. The base class expects two values two, separated by a comma.
This is a base class used for creating RANGE lookup filters. It behaves
identically to ``BaseInFilter`` with the exception that it expects only two
comma-separated values.
Example. ::

View File

@ -79,7 +79,19 @@ For Django version 1.8, transformed expressions are not supported.
.. _`lookup reference`: https://docs.djangoproject.com/en/dev/ref/models/lookups/#module-django.db.models.lookups
While both of these fields are optional, it is recommended that you specify
Common declarative problems
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Below are some of the common problem that occur when declaring filters. It is
recommended that you do read this as it provides a more complete understanding
on 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::
@ -92,14 +104,56 @@ lookup type. Under the hood, this will incorrectly be resolved as::
Produce.objects.filter(price__gt__exact=value)
.. note::
The above will most likely generate a ``FieldError``. The correct configuration
would be::
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.
class ProductFilter(django_filters.FilterSet):
price__gt = django_filters.NumberFilter(name='price', lookup_expr='gt')
For more information on additional arguments, see :ref:`core-arguments`.
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::
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::
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 model
field. The following would correctly allow you to search for both uncategorized
products and products for a set of categories::
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>`.
Generating filters with Meta.fields