Вы можете выполнять побитовые операции на уровне базы данных с F объектами .
Если поле неотрицательно, это означает, что условие field & mask > 0
может быть переписано как (field > 0) AND (field >= (field & mask))
. Если вы хотите проверить, применимы ли все биты mask
((field & mask) == mask
), вы можете создать предыдущее выражение для каждого бита, а затем объединить условия с помощью sql AND
. Пожалуйста, посмотрите пример, как это можно сделать. (Пользовательский QuerySet просто для удобства. Если вы используете старые версии django, вы можете реализовать has_one_of
и has_all
в качестве отдельных функций или методов класса, или лучше PathThroughManager ). Примечание 1 * F
- это обходной путь для принудительной установки скобок над побитовой операцией, в противном случае django (как для версии 1.5) выдаст неверный sql (colors >= colors & mask
, сравнение имеет более высокий приоритет, поэтому оно будет означать 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__gte=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()