Tastypie Negation Filter
Asked Answered
O

5

19

Is there a negation filter available by default. The idea is that you can do the following in the django ORM:

model.objects.filter(field!=value)

How can I do that in tastypie if that is even possible. I tried:

someapi.com/resource/pk/?field__not=value
someapi.com/resource/pk/?field__!=value
someapi.com/resource/pk/?field!=value

And all of them given me errors.

Oidea answered 12/3, 2012 at 21:33 Comment(1)
In some cases you have the ability to replace it with some filters like that: ?field__not=null can be replaced with ?field__isnull=false, "not greater than" can be replaced by just ?field__lte=x (so with "less than equal"). Also please keep in mind Django may somehow allow you to pass field!=value as an argument, but it will result in boolean value being passed further (or NameError if field is not a defined variable). Or am I wrong and Django performs operator overload as eg. web2py does in case of query builder?Peggie
W
28

Unfortunately there's not.

The problem is that Tastypie's ModelResource class uses the filter() method of the QuerySet only, i.e. it does not use exclude() which should be used for negative filters. There is no filter() field lookup that would mean negation though. The valid lookups are (after this SO post):

exact
iexact
contains
icontains
in
gt
gte
lt
lte
startswith
istartswith
endswith
iendswith
range
year
month
day
week_day
isnull
search
regex
iregex

However it shouldn't be so hard to implement the support for something like "__not_eq". All you need to do is to modify the apply_filters() method and separate filters with "__not_eq" from the rest. Then you should pass the first group to exclude() and the rest to filter().

Something like:

def apply_filters(self, request, applicable_filters):
    """
    An ORM-specific implementation of ``apply_filters``.

    The default simply applies the ``applicable_filters`` as ``**kwargs``,
    but should make it possible to do more advanced things.
    """
    positive_filters = {}
    negative_filters = {}
    for lookup in applicable_filters.keys():
        if lookup.endswith( '__not_eq' ):
            negative_filters[ lookup ] = applicable_filters[ lookup ]
        else:
            positive_filters[ lookup ] = applicable_filters[ lookup ]

    return self.get_object_list(request).filter(**positive_filters).exclude(**negative_filters)

instead of the default:

def apply_filters(self, request, applicable_filters):
    """
    An ORM-specific implementation of ``apply_filters``.

    The default simply applies the ``applicable_filters`` as ``**kwargs``,
    but should make it possible to do more advanced things.
    """
    return self.get_object_list(request).filter(**applicable_filters)

should allow for the following syntax:

someapi.com/resource/pk/?field__not_eq=value

I haven't tested it. It could probably be written in more elegant way too, but should get you going :)

Watts answered 16/3, 2012 at 21:13 Comment(1)
The key to the filter in negative_filters shouldn't be "field__not_eq", but "field__exact", so Django ORM modules may deal with it. Also, build_filters must be overwritten so "__not_eq" is not seen as a relationship by Tastypie.Lucubrate
V
6

Another way to do this without code changes is to use a iregex with inverse matching

http://HOST/api/v1/resource/?format=json&thing__iregex=^((?!notThis).)*$
Vetter answered 25/9, 2013 at 23:32 Comment(0)
I
2

I've opened a bug for this and provided a simple solution here: https://github.com/toastdriven/django-tastypie/issues/524

It would probably be nicer to add the '!' character at the end of the field name as you've done in your question ...

Indecorous answered 13/6, 2012 at 14:4 Comment(0)
S
1

Note on Gorneau's above answer: seems like this only works if you're not using a MySQL backend. See:

#1139 - Got error 'repetition-operator operand invalid' from regexp

Sworn answered 14/2, 2015 at 2:50 Comment(0)
J
-1

I use exclude() to avoid some values. For example:

Person.filter(name="Tim").exclude(state="Down");
Jemappes answered 15/2, 2016 at 14:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.