Using django-filter
-------------------

Django-filter provides a simple way to filter down a queryset based on
parameters a user provides.  Say we have a ``Product`` model and we want to let
our users filter which products they see on a list page.  Let's start with our
model::

    from django.db import models

    class Product(models.Model):
        name = models.CharField(max_length=255)
        price = models.DecimalField()
        description = models.TextField()
        release_date = models.DateField()
        manufacturer = models.ForeignKey(Manufacturer)

We have a number of fields and we want to let our users filter based on the
price or the release_date.  We create a ``FilterSet`` for this::

    import django_filters

    class ProductFilter(django_filters.FilterSet):
        class Meta:
            model = Product
            fields = ['price', 'release_date']


As you can see this uses a very similar API to Django's ``ModelForm``.  Just
like with a ``ModelForm`` we can also overide filters, or add new ones using a
declarative syntax::

    import django_filters

    class ProductFilter(django_filters.FilterSet):
        price = django_filters.NumberFilter(lookup_type='lt')
        class Meta:
            model = Product
            fields = ['price', 'release_date']

Filters take a ``lookup_type`` argument which specifies what lookup type to
use with Django's ORM.  So here when a user entered a price it would show all
Products with a price less than that.

Filters also take any arbitrary keyword arguments which get passed onto the
``django.forms.Field`` constructor.  These extra keyword arguments get stored
in ``Filter.extra``, so it's possible to overide the constructor of a
``FilterSet`` to add extra ones::

    class ProductFilterSet(django_filters.FilterSet):
        class Meta:
            model = Product
            fields = ['manufacturer']

        def __init__(self, *args, **kwargs):
            super(ProductFilterSet, self).__init__(*args, **kwargs)
            self.filters['manufacturer'].extra.update(
                {'empty_label': 'All Manufacturers'})


Now we need to write a view::

    def product_list(request):
        f = ProductFilter(request.GET, queryset=Product.objects.all())
        return render_to_response('my_app/template.html', {'filter': f})

If a queryset argument isn't provided then all the items in the default manager
of the model will be used.

And lastly we need a template::

    {% extends "base.html" %}

    {% block content %}
        <form action="" method="get">
            {{ filter.form.as_p }}
            <input type="submit" />
        </form>
        {% for obj in filter %}
            {{ obj.name }} - ${{ obj.price }}<br />
        {% endfor %}
    {% endblock %}

And that's all there is to it!  The ``form`` attribute contains a normal
Django form, and when we iterate over the ``FilterSet`` we get the objects in
the resulting queryset.

You can also allow the user to control ordering, this is done 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.

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.

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.``

Items in the ``fields`` sequence in the ``Meta`` class may include 
"relationship paths" using Django's ``__`` syntax to filter on fields on a 
related model.

If you want to use a custom widget, or in any other way overide the ordering
field you can overide the ``get_ordering_field()`` method on a ``FilterSet``.
This method just needs to return a Form Field.

Generic View
============

In addition to the above usage there is also a class-based generic view
included in django-filter, which lives at ``django_filters.views.FilterView``.
You must provide either a ``model`` or ``filter_class`` argument, similar to
``ListView`` in Django itself::

    # urls.py
    from django.conf.urls import patterns, url
    from django_filters.views import FilterView
    from myapp.models import Product
    
    urlpatterns = patterns('',
        (r'^list/', FilterView.as_view(model=Product)),
    )

You must provide a template at ``<app>/<model>_filter.html`` which gets the
context parameter ``filter``.  Additionally, the context will contain 
``object_list`` which holds the filtered queryset.

A legacy functional generic view is still include in django-filter, although
it's use is deprecated.  It can be found at 
``django_filters.views.object_filter``.  You must provide the same arguments
to it as the class based view::

    # urls.py
    from django.conf.urls import patterns, url
    from myapp.models import Product

    urlpatterns = patterns('',
        (r'^list/', 'django_filters.views.object_filter', {'model': Product}),
    )

The needed template and it's context variables will also be the same as the
class-based view above.
