Login

Bitwise operator queryset filter

Author:
[email protected]
Posted:
May 5, 2010
Language:
Python
Version:
1.1
Score:
0 (after 0 ratings)

This snippet for django-1.2 allows you to use bitwise operators without using QuerySet.extra()

from django.db.models import *
from somewhere import FQ

class BitWise(Model):
    type = CharField(max_length=8)
    value = IntegerField()

    def __unicode__(self):
        return '%s - %d' % (self.type, self.value)

>>> BitWise.objects.create(type='django', value=1)
<BitWise: django - 1>
>>> BitWise.objects.create(type='osso', value=3)
<BitWise: osso - 3>
>>> BitWise.objects.create(type='osso', value=7)
<BitWise: osso - 7>
>>> BitWise.objects.filter(FQ(F('value') & 1, 'gt', 0))
[<BitWise: django - 1>, <BitWise: osso - 3>, <BitWise: osso - 7>]
>>> BitWise.objects.filter(FQ(F('value') & 2, 'gt', 0))
[<BitWise: osso - 3>, <BitWise: osso - 7>]
>>> BitWise.objects.filter(FQ(F('value') & 1, 'gt', 0) & Q(type='django'))
[<BitWise: django - 1>]
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
from copy import deepcopy

from django.db import models
from django.db.models.sql.expressions import SQLEvaluator
from django.utils import tree

class FExpression(object):
    '''
    FExpression is the simplest way to reach the code path we need
    and should not inherit from tree.Node or any subclass thereof.

    FExpression currently only supports the integer comparison.
    Partial string comparison and such do not work because of the
    way these are defined in connection.operators. For those you
    should use the regular filter methods django provides.
    '''
    def __init__(self, left_expression, lookup_type, right_expression):
        self.left_expression, self.lookup_type, self.right_expression = \
                left_expression, lookup_type, right_expression

    def as_sql(self, qn, connection):
        cast_sql = connection.ops.lookup_cast(self.lookup_type)
        field = models.Field()
        # expression is either a smart expression or a user parameter
        if hasattr(self.left_expression, 'as_sql'):
            left_sql, left_params = self.left_expression.as_sql(qn, connection)
            left_sql = cast_sql % left_sql
        else:
            left_sql, left_params = cast_sql, field.get_db_prep_lookup(self.lookup_type, self.left_expression, connection=connection)
        # right hand side is cast by the operator sql
        if hasattr(self.right_expression, 'as_sql'):
            right_sql, right_params = self.right_expression.as_sql(qn, connection)
        else:
            right_sql, right_params = '%s', field.get_db_prep_lookup(self.lookup_type, self.right_expression, connection=connection)

        format = '%%s %s' % connection.operators[self.lookup_type]
        return format % (left_sql, right_sql), list(left_params) + list(right_params)


class FNode(tree.Node):
    '''
    FNode inherits tree.Node to pass tests for the code path we need
    This allows FNode to work without using complex_filter
    '''
    def __init__(self, left_expression, lookup_type, right_expression):
        if not lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte'):
            raise ValueError('Invalid operator, operator %r is not supported.' % lookup_type)
        super(FNode, self).__init__()
        self.left_expression, self.lookup_type, self.right_expression = \
                left_expression, lookup_type, right_expression

    def __deepcopy__(self, memodict):
        obj = super(FNode, self).__deepcopy__(memodict)
        obj.left_expression = deepcopy(self.left_expression, memodict)
        obj.right_expression = deepcopy(self.right_expression, memodict)
        obj.lookup_type = self.lookup_type
        return obj

    def add_to_query(self, query, aliases):
        # evaluate if it is a query expression
        if hasattr(self.left_expression, 'evaluate'):
            self.left_expression = SQLEvaluator(self.left_expression, query)
        if hasattr(self.right_expression, 'evaluate'):
            self.right_expression = SQLEvaluator(self.right_expression, query)
        query.where.add(FExpression(self.left_expression, self.lookup_type, self.right_expression), self.connector)


def FQ(*args, **kwargs):
    '''
    Wrap FNode in a Q object for it's logical operators
    '''
    return models.Q(FNode(*args, **kwargs))

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 10 months, 3 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
  5. Help text hyperlinks by sa2812 1 year, 7 months ago

Comments

mikesperanza (on August 16, 2013):

Very nice and useful for anyone stuck on pre-1.5

However, I found you also need to implement the relabel_aliases on the FExpression, otherwise, in subqueries, the aliases will not be used and the query will not run correctly. Something like this:

def relabel_aliases(self, change_map):
    if hasattr(self.left_expression, "relabel_aliases"):
        self.left_expression.relabel_aliases(change_map)
    if hasattr(self.right_expression, "relabel_aliases"):
        self.right_expression.relabel_aliases(change_map)

#

Please login first before commenting.