5👍
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):
# do not 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)
6👍
I think the best solution here would be for you to create a new field type by subclassing models.Field
. You could make use of the choices parameter to assign the valid bit flags and their meanings. This would help keep your model declaration clean and readable, with a final result along the lines of:
class BitFlagField(models.Field):
...
class MyModel(models.Model):
...
FLAG_CHOICES = (
(1, 'Blacklisted'),
(2, 'Special Guest'),
(4, 'Attend Ad-hoc Sessions'),
(8, 'Attend VIP Sessions'),
(16, 'Access Facility A'),
(32, 'Access Facility B'),
)
flags = BitFlagField(choices=FLAG_CHOICES)
...
The Django documentation has a great in-depth article on how to go about subclassing models.Field:
Writing Custom Model Fields
It seems to cover everything you need to do, including:
- Specifying Form Field (Tying a form to the field so that django-admin knows how to display it.)
- Preparing Values for Lookups (Which will allow you to use the field for searches and filtering.)
If you’re looking for an example of a subclassed field, this snippet might be of use. Its goal is similar (multiple choices as a model field), but its manner of storing them in the database differs (it’s using a CSV text field instead of bit flags).
- Paypal monthly subscription plan settings for first day of the month and making monthly recurring payment – django python
- Remove padding from matplotlib plotting
- Django: correctly retrieve data where date and time are greater than now
- Create a post activate script in Conda
- Google App Engine Application Extremely slow
6👍
A great tested solution, even if it doesn’t fit your model right away, would be using django-bitfield
1👍
This is how I would use the flags with my User class:
FLAGS = {
1:"Blacklisted",
2:"SpecialGuest",
4:"AttendAd-hocSessions",
8:"AttendVIPSessions",
16:"AccessFacilityA",
32:"AccessFacilityB",
}
class User(object):
def __init__(self, name="John Doe", groups=0):
self.name = name
self.groups = groups
def memberof(self):
''' Display string representation of the groups. '''
for flag in sorted(FLAGS):
if (flag & self.groups) == flag:
print FLAGS[flag]
Of course instead of printing the flags, you can create a comma-separated string to display in the admin view, or whatever you desire.
For the admin, just use a boolean for each of the group values.