I have a data model with a bitfield defined something like this:
alter table MemberFlags add column title varchar(50) not null default '';
alter table MemberFlags add column value integer( 3) not null default 0;
insert into MemberFlags (title, value) values
("Blacklisted", 1),
("Special Guest", 2),
("Attend Ad-hoc Sessions", 4),
("Attend VIP Sessions", 8),
("Access Facility A", 16),
("Access Facility B", 32)
And used like this:
alter table Membership add column title varchar(50) not null default '';
alter table Membership add column flags integer( 3) not null default 0;
insert into Membership (title, flags) values
("Guest Pass", 4+2 ),
("Silver Plan", 16+ 4 ),
("Gold Plan", 32+16+ 4+2 ),
("VIP Pass", 32+16+8+4+2 )
My questions are:
A) What's the easiest way to represent the different bitflags as separate items in the admin site? Should I override the template, or do something with forms?
B) How about the search list? I could create functions in the model to represent each bit, but how would searching and sorting be done?
I'm new to Django.
Working off the snippet in Andrew's answer, here are the changes you'd need to make:
from django.db import models
from django import forms
class BitFlagFormField(forms.MultipleChoiceField):
widget = forms.CheckboxSelectMultiple
def __init__(self, *args, **kwargs):
super(BitFlagFormField, self).__init__(*args, **kwargs)
class BitFlagField(models.Field):
__metaclass__ = models.SubfieldBase
def get_internal_type(self):
return "Integer"
def get_choices_default(self):
return self.get_choices(include_blank=False)
def _get_FIELD_display(self, field):
value = getattr(self, field.attname)
choicedict = dict(field.choices)
def formfield(self, **kwargs):
# don't call super, as that overrides default widget if it has choices
defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name),
'help_text': self.help_text, 'choices':self.choices}
if self.has_default():
defaults['initial'] = self.get_default()
defaults.update(kwargs)
return BitFlagFormField(**defaults)
def get_db_prep_value(self, value):
if isinstance(value, int):
return value
elif isinstance(value, list):
return sum(value)
def to_python(self, value):
result = []
n = 1
while value > 0:
if (value % 2) > 0:
result.append(n)
n *= 2
value /= 2
return sorted(result)
def contribute_to_class(self, cls, name):
super(BitFlagField, self).contribute_to_class(cls, name)
if self.choices:
func = lambda self, fieldname = name, choicedict = dict(self.choices):" and ".join([choicedict.get(value,value) for value in getattr(self,fieldname)])
setattr(cls, 'get_%s_display' % self.name, func)