Подтвердить что ты не робот

Лучшая практика для констант Python и Django

У меня есть модель Django, которая опирается на кортеж. Мне интересно, что лучше всего для ссылки на константы в этом кортеже для моей программы Django. Здесь, например, я хотел бы указать "default=0" как нечто более читаемое и не требующее комментирования. Любые предложения?

Status = (
    (-1, 'Cancelled'),
    (0, 'Requires attention'),
    (1, 'Work in progress'),
    (2, 'Complete'),
)

class Task(models.Model):
    status = models.IntegerField(choices=Status, default=0) # Status is 'Requires attention' (0) by default.

EDIT:

Если возможно, я бы вообще не хотел использовать номер. Как-то использование строки "Требуется внимание" вместо этого было бы более читаемым.

4b9b3361

Ответ 1

Достаточно часто определять константы для целочисленных значений следующим образом:

class Task(models.Model):
    CANCELLED = -1
    REQUIRES_ATTENTION = 0
    WORK_IN_PROGRESS = 1
    COMPLETE = 2

    Status = (
        (CANCELLED, 'Cancelled'),
        (REQUIRES_ATTENTION, 'Requires attention'),
        (WORK_IN_PROGRESS, 'Work in progress'),
        (COMPLETE, 'Complete'),
    )

    status = models.IntegerField(choices=Status, default=REQUIRES_ATTENTION)

Перемещая константы и Status внутри класса, вы сохраняете пространство имен модулей более чистым, и в качестве бонуса вы можете ссылаться на Tasks.COMPLETE везде, где вы импортируете модель Tasks.

Ответ 2

CANCELED, ATTENTION, WIP, COMPLETE = range(-1, 3)
Status = (
    (CANCELED, 'Cancelled'),
    (ATTENTION, 'Requires attention'),
    (WIP, 'Work in progress'),
    (COMPLETE, 'Complete'),
)

class Task(models.Model):
    status = models.IntegerField(choices=Status, default=CANCELED)


Имейте в виду, что, как отмечали другие, правильным способом является включение этих переменных внутри вашего класса модели. Это также, как это делает официальный django .

Существует только одна причина, по которой вы хотите поместить ее за пределы пространства имен классов и только в том случае, если эти семантики одинаково распределены другими моделями вашего приложения. то есть вы не можете решить, в какой конкретной модели они принадлежат.

Хотя, похоже, это не так в вашем конкретном примере.

Ответ 3

Вы можете использовать namedtuple, используя неизменяемый для константы.; -)

>>> from collections import namedtuple
>>> Status = namedtuple('Status', ['CANCELLED', 'REQUIRES_ATTENTION', 'WORK_IN_PROGRESS', 'COMPLETE'])(*range(-1, 3))
>>> Status
Status(CANCELLED=-1, REQUIRES_ATTENTION=0, WORK_IN_PROGRESS=1, COMPLETE=2)
>>> Status.CANCELLED
-1
>>> Status[0]
-1

Использование атрибутов на Task в качестве констант, таких как ответ Alasdair, имеет больше смысла в этом случае, но namedtuples - очень дешевые заменители для dicts и объектов, которые не изменение. Особенно удобно, если вы хотите, чтобы их было много в памяти. Они похожи на обычные кортежи с бонусом описательного __repr__ и доступа к атрибутам.

Ответ 4

Python 3.4+: Enum

Вы пишете "Если возможно, я бы вообще не хотел использовать номер". и действительно, именованное представление явно более питоновское. Голая строка, однако, восприимчива к опечаткам.

В Python 3.4 представлен модуль, называемый Enum, предоставляя псевдокассы Enum и IntEnum которые помогают в этой ситуации. С его помощью ваш пример может работать следующим образом:

# in Python 3.4 or later:
import enum  

class Status(enum.IntEnum):
    Cancelled = -1,
    Requires_attention = 0,
    Work_in_progress = 1,
    Complete = 2

def choiceadapter(enumtype):
    return ((item.value, item.name.replace('_', ' ')) for item in enumtype)

class Task(models.Model):
    status = models.IntegerField(choices=choiceadapter(Status), 
                                 default=Status.Requires_attention.value)

и как только команда Django набирает Enum, choiceadapter будет даже встроен в Django.

Ответ 5

Мой подход:

class Task(models.Model):
    STATUSES = { 'cancelled': 'Cancelled',
                 'requires attention': 'Requires attention',
                 'work in progress': 'Work in progress',
                 'complete': 'Complete' }

    status = models.CharField(choices=STATUSES.items(), default='cancelled')

Это позволяет вам писать удобные выражения:

tasks = Task.objects.filter(status='complete')

Кроме того, он позволяет не создавать ненужные глобальные переменные.

Если вы действительно хотите использовать целое поле:

class Task(models.Model):

   class STATUS:
      CANCELED, ATTENTION, WIP, COMPLETE = range(-1, 3)
      choices = {
        CANCELED: 'Cancelled',
        ATTENTION: 'Requires attention',
        WIP: 'Work in progress',
        COMPLETE: 'Complete'
      }


   status = models.CharField(choices=STATUSES.choices.items(), default=STATUSES.CANCELED)

и

tasks = Task.objects.filter(status=Task.STATUSES.COMPLETE)

Ответ 6

Вы можете использовать словарь для небольшого улучшения ясности:

Status = {
    -1: 'Cancelled',
    0: 'Requires attention',
    1: 'Work in progress',
    2: 'Complete',
}

class Task(models.Model):
    status = models.IntegerField(choices=Status.items(), default=Status[0])

Ответ 7

Я не использую Django, но я делаю что-то вроде следующего довольно немного под Pyramid и Twisted...

def setup_mapping( pairs ):
    mapping = {'id':{},'name':{}}
    for (k,v) in pairs:
        mapping['id'][k]= v
        mapping['name'][v]= k
    return mapping

class ConstantsObject(object):
    _pairs= None
    mapping= None

    @classmethod
    def lookup_id( cls , id ):
       pass

    @classmethod
    def lookup_name( cls , name ):
       pass

class StatusConstants(ConstantsObject):
    CANCELLED = -1
    REQUIRES_ATTENTION = 0
    WORK_IN_PROGRESS = 1
    COMPLETE = 2

    _pairs= (
        (-1, 'Cancelled'),
        (0, 'Requires attention'),
        (1, 'Work in progress'),
        (2, 'Complete'),
    )
    mapping= setup_mapping(_pairs)

Итак, суть такова:

  • Существует базовый класс "константы" и еще один класс для каждого типа. класс определяет ключевые слова для значения в ALLCAPS
  • Я вбрасываю в открытый текст _pairs в класс. Зачем? потому что мне может понадобиться построить с ними несколько таблиц DB, или я могу захотеть их для сообщений об ошибках/статусах. Я использую числа, а не имя переменной ALLCAPS, как личное предпочтение.
  • я инициализирует переменную класса mapping, которая в основном обезьяна передает класс, предварительно скомпилировав кучу переменных внутри dict, потому что...
  • класс выводится из этого базового класса, который предлагает функциональные возможности класса для поиска значения или других стандартных действий, которые вам часто нужно делать с константами.

Это не одноразовый подход, но мне, как правило, очень нравится это. Вы можете легко использовать dict для определения пар, чтобы функция "mapping" установила некоторые другие атрибуты, например, давая вам кортежи значений пары как k, v или v, k или любой странный формат, который вам может понадобиться.

мой код может выглядеть следующим образом:

status_id = sa.Column(sa.Integer, sa.ForeignKey("_status.id") , nullable=False , default=constants.StatusConstants.CANCELLED )

status_name = constants.StatusConstants.lookup_id(status_id)    
status_name = constants.StatusConstants.mapping['id'][status_id]

когда вам нужно использовать константы по-другому, вы просто добавляете или изменяете методы класса базы.

Ответ 8

Иногда мне нужно создать огромный список выбора. Мне не нравится набирать текст как обезьяна, поэтому я предпочитаю создать такую ​​функцию:

def choices(labels):
    labels = labels.strip().split('\n')
    ids = range(1, len(labels)+1)
    return zip(ids, labels)

И используйте вот так:

my_choices = """
choice1
choice2
choice3
"""
MY_CHOICES = choices(my_choices)
print(MY_CHOICES) # ((1, choice1), (2, choice2), (3, choice3))