Как я должен представлять битовые флаги int field в django admin?
У меня есть модель данных с битовым полем, определенным примерно так:
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)
И используется так:
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 )
Мои вопросы:
А) Какой самый простой способ представить разные битовые флаги как отдельные элементы на сайте администратора? Должен ли я переопределить шаблон или сделать что-то с формами?
Б) Как насчет списка поиска? Я мог бы создать функции в модели для представления каждого бита, но как будет выполняться поиск и сортировка?
Я новичок в Джанго.
4 ответа
Работая над фрагментом ответа Эндрю, вот изменения, которые вам нужно сделать:
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)
Я думаю, что лучшее решение здесь было бы для вас, чтобы создать новый тип поля с помощью подклассов models.Field
, Вы можете использовать параметр choices, чтобы назначить действительные битовые флаги и их значения. Это поможет сохранить вашу модель декларации в чистоте и удобочитаемом виде с конечным результатом в соответствии с:
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)
...
В документации Django есть отличная глубокая статья о том, как работать с подклассами моделей. Поле:
Написание пользовательских полей модели
Кажется, он охватывает все, что вам нужно сделать, в том числе:
- Указание поля формы (привязка формы к полю, чтобы django-admin знал, как ее отобразить.)
- Подготовка значений для поиска (что позволит вам использовать поле для поиска и фильтрации).
Если вы ищете пример подклассового поля, этот фрагмент может быть полезен. Его цель аналогична (множественный выбор в качестве поля модели), но способ хранения их в базе данных отличается (он использует текстовое поле CSV вместо битовых флагов).
Отличным протестированным решением, даже если оно не подходит для вашей модели сразу же, будет использование django-bitfield
Вот как я бы использовал флаги с моим классом User:
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]
Конечно, вместо того, чтобы печатать флаги, вы можете создать разделенную запятыми строку для отображения в представлении администратора или что угодно.
Для администратора просто используйте логическое значение для каждого значения группы.