Created
June 8, 2012 21:33
-
-
Save emidln/2898209 to your computer and use it in GitHub Desktop.
django boolean querying api from json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# this is still missing error handling | |
# it's very convenient to convert json arrays into python lists letting javascript clients | |
# get powerful querying | |
# if you wrap up the full message with something like a meta field to include options | |
# for limiting, offsetting, format, orderying, sorting, etc, you have a full-blown | |
# mini api with powerful querying | |
# the else part of evaluate should be a function and should provide a callback so that | |
# certain filter_names or data types can be controlled better | |
import operator | |
from django.db.models import Q | |
class InvalidExpression(Exception): pass | |
def evaluate(expr): | |
""" evaluates expression returning a Q object suitable for passing to an orm filter() | |
filter expressions look like: | |
["field_name", "filter_name", "value0", "value1"] | |
field_name is a string representing a field in your model | |
filter_name is a valid filter for your field's type (e.g. lte for an IntegerField) | |
values are one or more values as required by the filter name | |
An Example from the basic polling app in the django tutorial (querying the Choice model): | |
["poll__question", "startswith", "how many"] | |
["votes", "gte", 4] | |
["pk", "in", 1, 2, 3, 4, 5] | |
Boolean constructs AND, OR, and NOT are legal as well: | |
["and|or" [ONE OR MORE EXPRESSIONS]] | |
["not" [SINGLE EXPRESSION]] | |
Examples (still based on the Choice model from the django tutorial): | |
["and", | |
["pub_date", "range", "2012-5-1", "2012-6-1"], | |
["or", | |
["poll__question", "icontains", "pikachu"], | |
["poll__question", "icontains", "bulbasaur"], | |
["votes", "gte", 100]]] | |
""" | |
if not expr: | |
return Q() | |
try: | |
car, cdr = expr[0].lower(), expr[1:] | |
except ValueError: | |
raise InvalidExpression(expr) | |
if car == 'and': | |
return reduce(operator.and_, map(evaluate, cdr)) | |
elif car == 'or': | |
return reduce(operator.or_, map(evaluate, cdr)) | |
elif car == 'not': | |
return operator.not_(evaluate(cdr)) | |
else: | |
values = cdr[1:] | |
if len(values) == 1: | |
values = values[0] | |
# there should be exception handling and a transform callback here so you can preprocess certain ops | |
return Q(**{'__'.join((car,cdr[0].lower())):values}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment