Skip to content

Instantly share code, notes, and snippets.

@emidln
Created June 8, 2012 21:33
Show Gist options
  • Save emidln/2898209 to your computer and use it in GitHub Desktop.
Save emidln/2898209 to your computer and use it in GitHub Desktop.
django boolean querying api from json
# 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