6. Filter Products by its Attributes¶
Besides Full Text Search, adding some filter functionality to an e-commerce site is another very important feature. Customers must be able to narrow down the list of available products to a set of desired products using a combination of prepared filter attributes.
Since in djangoSHOP each product class declares its own database model with its own attributes, often related with foreign data models, filtering must be implemented by the merchant on top of the existing product models. Fortunately the REST framework in combination with `Django Filter`_ makes this a rather simple task.
6.1. Adding a filter to the List View¶
In djangoSHOP listing the products normally is controlled by
shop.views.catalog.ProductListView
or shop.views.catalog.CMSPageProductListView
.
By default these View classes are configured to use the default filter backends as provided by the
REST framework. These filter backends can be configured globally through the settings variable
DEFAULT_FILTER_BACKENDS
.
Additionally we can subclass the filter backends for each View class in our urls.py
. Say, we
need a special catalog filter, which groups our products by a certain product attribute. Then we
can create customized filter backend
from rest_framework.filters import BaseFilterBackend
class CatalogFilterBackend(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
queryset = queryset.order_by('attribute__sortmetric')
return queryset
In urls.py
, where we route requests to the class shop.views.catalog.ProductListView
,
we then replace the default filter backends by our own implementation:
from django.conf.urls import patterns, url
from rest_framework.settings import api_settings
from shop.views.catalog import ProductListView
from myshop.serializers import ProductSummarySerializer
urlpatterns = patterns('',
url(r'^$', ProductListView.as_view(
serializer_class=ProductSummarySerializer,
filter_backends=[CatalogFilterBackend],
),
)
The above example is very simple but gives a rough impression on its possibilities.
6.1.1. Working with Django-Filter¶
django-filter is a generic, reusable application to alleviate writing some of the more mundane bits of view code. Specifically, it allows users to filter down a queryset based on a model’s fields, displaying the form to let them do this.
REST framework also includes support for generic filtering backends that allow you to easily construct complex searches and filters.
By creating a class which inherit from django_filters.FilterSet
, we can build filters
against each attribute of our product. This filter then uses the passed in query parameters to
restrict the set of products available from our catalog:
import django_filters
class ProductFilter(django_filters.FilterSet):
width = django_filters.RangeFilter(name='width')
props = django_filters.MethodFilter(action='filter_properties', widget=SelectMultiple)
class Meta:
model = OurProduct
fields = ['width', 'props']
def filter_properties(self, queryset, values):
for value in values:
queryset = queryset.filter(properties=value)
return queryset
This example assumes that OurProduct
has a numeric attribute named width
and a many-to-many
field named properties
.
We then can add this filter to the list view for our products. In djangoSHOP we normally do this through the url patterns:
urlpatterns = patterns('',
url(r'^$', ProductListView.as_view(
serializer_class=ProductSummarySerializer,
filter_class=ProductFilter,
)),
# other patterns
)
By appending ?props=17
to the URL, the above filter class will restrict the products in our list
view to those with a property
of 17.