在Django模型中表示工作日的多选字段

时间:2010-09-08 01:05:11

标签: python django django-models

我一直在寻找一种优雅的方式来代表Django模型中的多选工作日字段(周一,周二,周三......)。我最初想的是使用按位数学来进行整数字段,但我不确定这是否可行。

这将是一个主要阅读的领域。我希望Queryset方法类似于Entry.objects.get(weekdays__contains=MONDAY),其中MONDAY将是常量。

也许有人可以提出更好的解决方案?或者也许有人做过类似的事情并且有一些他们可以贡献的示例代码?

1 个答案:

答案 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)来允许用户在管理员中选择其值。