I want to filter results in the tastypie to get results that conform to both of two filters on the same field.
So if I have a simple model like this...
class Item(models.Model):
name = models.CharField(max_length=255)
description = models.TextField()
With a ModelResource...
class ItemResource(ModelResource):
...
class Meta():
queryset = Item.objects.all()
resource_name = 'item'
filtering = {'name': ALL, 'description': ALL}
I can easily construct 'AND' queries in the url of tastypie:
/api/v1/item/?name__contains=hello&description__contains=foo
But if I want to construct an AND operator on the same field, it only takes the second argument and ignores the first. That is,
/api/v1/item/?name__contains=hello&name__contains=world
returns resources whose name field contains 'world' but not those whose name field contains BOTH 'hello' and 'world'.
I understand how to do this directly in django:
Item.objects.filter(name__contains='hello').filter(name__contains='world')
But how do I construct this kind of a query in the URL of the tastypie?
I'm using the below. It will give you support for name__contains=hello,world
. And you could also do negations name__contains!=foo
.
def build_filters(self, filters=None):
"""
Adds support for negation filtering
"""
if not filters:
return filters
applicable_filters = {}
self.filters = filters
# Normal filtering
filter_params = dict([(x, filters[x]) for x in filter(lambda x: not x.endswith('!'), filters)])
applicable_filters['filter'] = super(MainBaseResource, self).build_filters(filter_params)
# Exclude filtering
exclude_params = dict([(x[:-1], filters[x]) for x in filter(lambda x: x.endswith('!'), filters)])
applicable_filters['exclude'] = super(MainBaseResource, self).build_filters(exclude_params)
return applicable_filters
def apply_filters(self, request, applicable_filters):
"""
Adds support for:
1. negation filtering: value_date__year!=2013
2. multiple filtering value_date__year=2013,2012
"""
from django.db.models import Q
import operator
from types import *
objects = self.get_object_list(request)
f = applicable_filters.get('filter')
if f:
# Q Filters for multiple values (1,2,3 etc)
q_filters = []
for key, val in f.iteritems():
string = str(val)
if ',' in string:
for excl_filter in string.split(','):
q_filters.append((key, excl_filter))
q_list = [Q(x) for x in q_filters]
for x in q_filters:
try:
del f[x[0]]
except:
pass
if q_list:
objects = objects.filter(reduce(operator.or_, q_list), **f)
else:
objects = objects.filter(**f)
e = applicable_filters.get('exclude')
if e:
objects = objects.exclude(**e)
return objects