Here's some code we use at Disqus.

def model_bitfield_handler(attr_name, bit_number):
    mask = 2**bit_number
    def _bit_handler(self, new_val=None):
        if new_val is not None:
            old_val = (getattr(self, attr_name, 0) or 0)
            if new_val:
                setattr(self, attr_name, old_val | mask)
            else:
                setattr(self, attr_name, old_val & (~mask))
        cur_val = (getattr(self, attr_name, 0) or 0)
        return cur_val & mask
    return _bit_handler

settings_bit_handler = functools.partial(model_bitfield_handler,
'settings')
flags_bit_handler = functools.partial(model_bitfield_handler, 'flags')

class SomeModel(models.Model):
    IS_FOO = flags_bit_handler(0)
    IS_BAR = flags_bit_handler(1)

You use it like instance.IS_FOO() to get the value, or instance.IS_FOO
(True) to set it.
It might be nice to use properties instead.

The following code lets you do ORM filter queries on bitfields. It's
just slightly modified from something Malcolm wrote for us. It lets
you do SomeModel.objects.filter(BitCheck('flags', 2**4)) for example.

class BitCheck(Node):
    """
    An object kind of like django.db.models.Q that can be used in
filters to
    perform bit-field checks. Constructed with the name of a field
(the model
    attribute name) and the value to "and" against (1, 2, 4, etc...).

    NOTE: This only works on fields that exist on the base model of
the
    queryset/query. It currently does not follow "foo__bar__baz" field
names.
    """
    def __init__(self, name, value, connector=sql.AND, **kwargs):
        """
        - ``name`` is the model attribute name.
        - ``value`` is the value to "and" against.
        - ``connector`` is AND or OR; the type of connection to make
with other
          elements in the where-clause.
        """
        self.name = name
        self.value = value
        super(BitCheck, self).__init__(connector=connector, **kwargs)

    def __deepcopy__(self, memodict):
        obj = super(BitCheck, self).__deepcopy__(memodict)
        obj.name = self.name
        obj.value = self.value
        return obj

    def add_to_query(self, query, used_aliases):
        """
        Insert the correct SQL into the query's where-clause.

        This is called by Query.add_q().
        """
        # TODO: To support foo__bar__baz style names or even literal
column
        # names, this is where alterations have to happen. All we are
doing
        # here is extracting the correct table alias and column name.
        alias = query.get_initial_alias()
        column = query.model._meta.get_field_by_name(self.name)
[0].column
        w = BitCheckWhere(alias, column, self.value)
        query.where.add(w, self.connector)

class BitCheckWhere(object):
    def __init__(self, alias, column, value):
        self.table_alias = alias
        self.column = column
        self.value = value

    def relabel_aliases(self, change_map):
        """
        Called if table aliases are changing in the main query. The
        "change_map" parameter is a mapping of old --> new alias
names.

        This will be called by Where.relabel_aliases().
        """
        self.table_alias = change_map.get(self.table_alias,
self.table_alias)

    def as_sql(self, qn):
        """
        Create the proper SQL fragment. This inserts something like
        "bool(T0.flags & value)".

        This will be called by Where.as_sql()
        """
        return ("bool (%s.%s & %s)" % (qn(self.table_alias), qn
(self.column), self.value),
                [])

Can anyone come up with a way to combine setting the model-attribute
names with creating constants somewhere for that attribute's bitmask?
So that you could somehow, approximately go BitCheck('flags', IS_FOO)

Anyways, hope this all helps.

Andrew

On Dec 4, 2:16 pm, "Craig Kimerer" <[EMAIL PROTECTED]> wrote:
> Apologies if this has been asked already and I have missed it in searching,
> but is there any interest in taking a patch for a BitmaskField?
>
> Given the following (albeit stupid) example to show some usages that would
> be nice to have on a bitmask field.  I should note in the examples below,
> the names I have chosen I am not sold on, but work well enough to describe
> what is going on in this example.
>
> class Person(models.Model):
> name = models.TextField()
> flags = models.BitmaskField()
>
> class PeopleFlags(object):
> NoFlag = 0
> Male = 1
> Female = 2
> Student = 4
> Unemployed = 8
> Employed = 16
>
> Example filter API:
>
> Finding all unemployed students:
> Person.objects.filter(flags__all=[PeopleFlags.Unemployed,
> PeopleFlags.Student])
>
> Finding all females who are students or unemployed
> Person.objects.filter(flags__is=PeopleFlags.Female).filter(flags__any=[PeopleFlags.Student,PeopleFlags.Unemployed])
>
> Obviously there are some special cases, like you couldn't use the same logic
> if someone wanted to find 'All people with NoFlags'.  By default 0 would
> have to be special cased for '= 0' instead of '& 0'.
>
> I dont have the code currently written, but I am willing to put some work
> into it if this is a feature that people (other than me) think would be
> useful.
>
> Craig
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to django-developers@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to