Представление поля множественного выбора для рабочих дней в модели Django - PullRequest
8 голосов
/ 08 сентября 2010

Я искал элегантный способ для представления поля выбора дня недели (понедельник, вторник, среда ...) в модели Django.Первоначально я думал о переходе к целочисленному полю с использованием побитовой математики, но я не уверен, что это будет путь.

Это поле будет в основном для чтения.Я бы хотел, чтобы метод Queryset был что-то вроде Entry.objects.get(weekdays__contains=MONDAY) Где MONDAY было бы константой.

Возможно, кто-то может найти лучшее решение?Или, может быть, кто-то сделал что-то подобное и имеет пример кода, который они могли бы внести?

1 Ответ

14 голосов
/ 16 ноября 2011

Это старый вопрос, но я подумал, что покажу, как это можно сделать достаточно просто в Джанго.

Вот вспомогательный класс для подготовки вашего выбора:

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), чтобы позволить пользователю выбирать свои значения в админке.

...