[Fixed]-How to perform DB bitwise queries in Django?

12๐Ÿ‘

You can perform database-level bitwise operations with F objects.

If field is non-negative, it means that condition field & mask > 0 can be re-written as (field > 0) AND (field >= (field & mask)). If you want to check if all bits of mask apply ((field & mask) == mask), you can build previous expression for each bit and then merge conditions via sql AND. Please see example how it can be done. (Custom QuerySet is just for convenience. If you use old django versions you can implement has_one_of and has_all as separate functions or classmethods, or better PathThroughManager). Note 1 * F is a workaround to force parenthesis over bitwise operation, otherwise django (as for version 1.5) will produce bad sql (colors >= colors & mask, comparison has higher priority, so it will mean TRUE & mask)

import operator
from django.db import models
from django.db.models import Q, F

_bit = lambda x: 2**(x-1)
RED = _bit(1)
GREEN = _bit(2)
BLUE = _bit(3)
WHITE = _bit(4)


class ItemColorsQuerySet(models.QuerySet):

    def has_one_of(self, colors):
        """
            Only those that has at least one of provided colors
        """
        return self.filter(
            colors__gt=0,
            # field value contains one of supplied color bits
            colors__lt=F('colors') + (1 * F('colors').bitand(reduce(operator.or_, colors, 0)))
        )

    def has_all(self, colors):
        """
            Has all provided colors (and probably others)
        """
        # filter conditions for all supplied colors: 
        # each one is "field value has bit that represents color"
        colors_q = map(lambda c: Q(colors__gte=1 * F('colors').bitand(c)), colors)
        # one complex Q object merged via sql AND:
        # colors>0 and all color-bit conditions
        filter_q = reduce(operator.and_, colors_q, Q(colors__gt=0))
        return self.filter(filter_q)


class Item(models.Model):

    name = models.CharField(max_length=100, unique=True)
    # can handle many colors using bitwise logic. Zero means no color is set.
    colors = models.PositiveIntegerField(default=0)

    objects = ItemColorsQuerySet.as_manager()
๐Ÿ‘คIvan Klass

11๐Ÿ‘

same like @Ivan Klass,

You can use bitand or bitor from django orm F,

# filter Red and Blue(0101)
Color.objects.annotate(
  color_filter=F('color').bitand(0101)
).filter(color_filter__gte=0101)

 # filter Red or Blue(0101)
Color.objects.annotate(
  color_filter=F('color').bitand(0101)
).filter(color_filter__gt=0)   
๐Ÿ‘คgaozhidf

10๐Ÿ‘

For postgres db you might use .extra() params with django orm.

For example:

SomeModel.objects.extra(where=['brand_label & 3 = 3'])
๐Ÿ‘คjbdi

7๐Ÿ‘

Check django-bitfield , it works well w/ PostgreSQL (probably also well MySQL)

๐Ÿ‘คokm

Leave a comment