我一直在寻找一种优雅的方式来代表Django模型中的多选工作日字段(周一,周二,周三......)。我最初想的是使用按位数学来进行整数字段,但我不确定这是否可行。
这将是一个主要阅读的领域。我希望Queryset方法类似于Entry.objects.get(weekdays__contains=MONDAY)
,其中MONDAY
将是常量。
也许有人可以提出更好的解决方案?或者也许有人做过类似的事情并且有一些他们可以贡献的示例代码?
答案 0 :(得分:14)
这是一个老问题,但我想我会说明如何在Django中合理地完成它。
这是一个帮助您准备选择的辅助类:
class BitChoices(object):
def __init__(self, choices):
self._choices = []
self._lookup = {}
for index, (key, val) in enumerate(choices):
index = 2**index
self._choices.append((index, val))
self._lookup[key] = index
def __iter__(self):
return iter(self._choices)
def __len__(self):
return len(self._choices)
def __getattr__(self, attr):
try:
return self._lookup[attr]
except KeyError:
raise AttributeError(attr)
def get_selected_keys(self, selection):
""" Return a list of keys for the given selection """
return [ k for k,b in self._lookup.iteritems() if b & selection]
def get_selected_values(self, selection):
""" Return a list of values for the given selection """
return [ v for b,v in self._choices if b & selection]
使用PositiveIntegerField定义您的模型,以及您想要的选择:
WEEKDAYS = BitChoices((('mon', 'Monday'), ('tue', 'Tuesday'), ('wed', 'Wednesday'),
('thu', 'Thursday'), ('fri', 'Friday'), ('sat', 'Saturday'),
('sun', 'Sunday')
))
这意味着您可以访问以下值:
>>> print list(WEEKDAYS)
[(1, 'Monday'), (2, 'Tuesday'), (4, 'Wednesday'), (8, 'Thursday'), (16, 'Friday'), (32, 'Saturday'), (64, 'Sunday')]
>>> print WEEKDAYS.fri
16
>>> print WEEKDAYS.get_selected_values(52)
['Wednesday', 'Friday', 'Saturday']
现在使用PositiveIntegerField
和以下选项定义您的模型:
class Entry(models.Model):
weekdays = models.PositiveIntegerField(choices=WEEKDAYS)
你的模特已经完成了。对于查询,以下是诀窍:
Entry.objects.extra(where=["weekdays & %s"], params=[WEEKDAYS.fri])
可能有一种方法可以创建一个整齐地打包查询的Q()
对象子类,所以它们看起来像这样:
Entry.objects.filter(HasBit('weekdays', WEEKDAYS.fri))
或者甚至破解F()
子类来创建这样的东西:
Entry.objects.filter(weekdays=HasBit(WEEKDAYS.fri))
但我现在没时间探索这个问题。 .where
工作正常,可以抽象为查询集函数。
最后一个考虑因素是,您可以点亮以创建自定义模型字段,将数据库中的位掩码转换为列表或在Python中设置。然后,您可以使用SelectMultiple
窗口小部件(或CheckboxSelectMultiple
)来允许用户在管理员中选择其值。