Как указать и использовать ENUM в модели Django?
Указание ENUM mySQL в модели Django
Ответы (9)
Из документации Django :
MAYBECHOICE = (
('y', 'Yes'),
('n', 'No'),
('u', 'Unknown'),
)
И вы определяете charfield в своей модели:
married = models.CharField(max_length=1, choices=MAYBECHOICE)
Вы можете сделать то же самое с целочисленными полями, если вам не нравятся буквы в вашей базе данных.
В этом случае перепишите свой выбор:
MAYBECHOICE = (
(0, 'Yes'),
(1, 'No'),
(2, 'Unknown'),
)
Использование параметра choices
не будет использовать тип базы данных ENUM; он просто создаст VARCHAR или INTEGER, в зависимости от того, используете ли вы choices
с CharField или IntegerField. Как правило, это просто прекрасно. Если для вас важно, чтобы тип ENUM использовался на уровне базы данных, у вас есть три варианта:
- Используйте «./manage.py sql appname», чтобы увидеть, как SQL генерирует Django, вручную измените его, чтобы использовать тип ENUM, и запустите его самостоятельно. Если вы сначала создадите таблицу вручную, «./manage.py syncdb» не будет с ней связываться.
- Если вы не хотите делать это вручную каждый раз при создании БД, поместите некоторый пользовательский SQL в appname/sql/modelname.sql для выполнения соответствующей команды ALTER TABLE.
- Создайте пользовательский тип поля и соответствующим образом определите метод db_type.
При любом из этих вариантов вы должны будете учитывать последствия для переносимости между базами данных. В варианте 2 вы можете использовать database-backend-specific пользовательский SQL, чтобы ваша инструкция ALTER TABLE выполнялась только в MySQL. В варианте 3 ваш метод db_type должен будет проверить механизм базы данных и установить тип столбца db на тип, который фактически существует в этой базе данных.
ОБНОВЛЕНИЕ: поскольку в Django 1.7 была добавлена среда миграции, варианты 1 и 2 выше полностью устарели. Вариант 3 всегда был лучшим вариантом в любом случае. Новая версия вариантов 1/2 будет включать сложную пользовательскую миграцию с использованием SeparateDatabaseAndState
, но на самом деле вам нужен вариант 3.
http://www.b-list.org/weblog/2007/nov/02/handle-choices-right-way/
class Entry(models.Model): LIVE_STATUS = 1 DRAFT_STATUS = 2 HIDDEN_STATUS = 3 STATUS_CHOICES = ( (LIVE_STATUS, 'Live'), (DRAFT_STATUS, 'Draft'), (HIDDEN_STATUS, 'Hidden'), ) # ...some other fields here... status = models.IntegerField(choices=STATUS_CHOICES, default=LIVE_STATUS) live_entries = Entry.objects.filter(status=Entry.LIVE_STATUS) draft_entries = Entry.objects.filter(status=Entry.DRAFT_STATUS) if entry_object.status == Entry.LIVE_STATUS:
Это еще один приятный и простой способ реализации перечислений, хотя на самом деле он не сохраняет перечисления в базе данных.
Однако он позволяет вам ссылаться на «метку» при каждом запросе или указании значений по умолчанию, в отличие от ответа с самым высоким рейтингом, где вы должны использовать «значение» (которое может быть числом).
Установка choices
в поле разрешит некоторую проверку на стороне Django, но не будет определять какую-либо форму перечисляемого типа на стороне базы данных.
Как уже упоминалось, решение состоит в том, чтобы указать db_type
в пользовательском поле.
Если вы используете серверную часть SQL (например, MySQL), вы можете сделать это следующим образом:
from django.db import models
class EnumField(models.Field):
def __init__(self, *args, **kwargs):
super(EnumField, self).__init__(*args, **kwargs)
assert self.choices, "Need choices for enumeration"
def db_type(self, connection):
if not all(isinstance(col, basestring) for col, _ in self.choices):
raise ValueError("MySQL ENUM values should be strings")
return "ENUM({})".format(','.join("'{}'".format(col)
for col, _ in self.choices))
class IceCreamFlavor(EnumField, models.CharField):
def __init__(self, *args, **kwargs):
flavors = [('chocolate', 'Chocolate'),
('vanilla', 'Vanilla'),
]
super(IceCreamFlavor, self).__init__(*args, choices=flavors, **kwargs)
class IceCream(models.Model):
price = models.DecimalField(max_digits=4, decimal_places=2)
flavor = IceCreamFlavor(max_length=20)
Запустите syncdb
и проверьте свою таблицу, чтобы убедиться, что ENUM
создан правильно.
mysql> SHOW COLUMNS IN icecream;
+--------+-----------------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------+-----------------------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| price | decimal(4,2) | NO | | NULL | |
| flavor | enum('chocolate','vanilla') | NO | | NULL | |
+--------+-----------------------------+------+-----+---------+----------------+
'type "enum" does not exist LINE 1: ....tablename" ADD COLUMN "select_user" ENUM('B', ...'
.
- person Grijesh Chauhan; 24.01.2014
Если вы действительно хотите использовать свои базы данных, введите ENUM:
- Используйте Джанго 1.x
- Признайте, что ваше приложение будет работать только с некоторыми базами данных.
- Поразмышляйте над этой страницей документации:http://docs.djangoproject.com/en/dev/howto/custom-model-fields/#howto-custom-model-fields
Удачи!
В настоящее время есть два проекта github, основанные на их добавлении, хотя я не изучал, как именно они реализованы:
- Django-EnumField:
Предоставляет поле модели перечисления Django (с использованием IntegerField) с многократно используемыми перечислениями и подтверждение перехода. - Django-EnumFields:
Этот пакет позволяет использовать настоящие перечисления Python (в стиле PEP435) с Джанго.
Я не думаю, что они используют типы перечислений БД, но они в работе для первого.
Django 3.0 имеет встроенную поддержку Enums.
Из документации:
from django.utils.translation import gettext_lazy as _
class Student(models.Model):
class YearInSchool(models.TextChoices):
FRESHMAN = 'FR', _('Freshman')
SOPHOMORE = 'SO', _('Sophomore')
JUNIOR = 'JR', _('Junior')
SENIOR = 'SR', _('Senior')
GRADUATE = 'GR', _('Graduate')
year_in_school = models.CharField(
max_length=2,
choices=YearInSchool.choices,
default=YearInSchool.FRESHMAN,
)
Имейте в виду, что он не применяет выбор на уровне базы данных, это только конструкция Python. Если вы хотите также применить эти значения в базе данных, вы можете объединить это с ограничениями базы данных:
class Student(models.Model):
...
class Meta:
constraints = [
CheckConstraint(
check=Q(year_in_school__in=YearInSchool.values),
name="valid_year_in_school")
]
В верхней части вашего файла models.py добавьте эту строку после импорта:
enum = lambda *l: [(s,_(s)) for s in l]