Указание ENUM mySQL в модели Django

Как указать и использовать ENUM в модели Django?


person Steve    schedule 21.08.2008    source источник
comment
Стив, если вы имели в виду тип MySQL ENUM, то вам не повезло, насколько я знаю, Django не поддерживает это (эта функция доступна не во всех БД, поддерживаемых Django). Ответ, предоставленный Полом, работает, но он не определяет тип в БД.   -  person dguaraglia    schedule 22.08.2008


Ответы (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'),
)
person fulmicoton    schedule 21.08.2008
comment
Это не предотвращает сохранение ложных значений, если они не были очищены ранее, не так ли? - person Strayer; 11.06.2012
comment
@Strayer да, я думаю, это полезно только для использования форм моделей - person moriesta; 17.03.2013
comment
Обратите внимание, что рекомендуемый стиль Django подразумевает, что символы должны быть константами: docs.djangoproject.com/en/dev/internals/contributing/ - person Michael Scheper; 03.12.2014
comment
Как сказал @Carl Meyer в своем ответе, это НЕ создает столбец ENUM в базе данных. Он создает столбец VARCHAR или INTEGER, поэтому на самом деле не отвечает на вопрос. - person Ariel; 15.09.2015
comment
Могу ли я добавить функцию выбора с целочисленным полем? @фулмикотон - person Ilyas karim; 18.03.2018

Использование параметра choices не будет использовать тип базы данных ENUM; он просто создаст VARCHAR или INTEGER, в зависимости от того, используете ли вы choices с CharField или IntegerField. Как правило, это просто прекрасно. Если для вас важно, чтобы тип ENUM использовался на уровне базы данных, у вас есть три варианта:

  1. Используйте «./manage.py sql appname», чтобы увидеть, как SQL генерирует Django, вручную измените его, чтобы использовать тип ENUM, и запустите его самостоятельно. Если вы сначала создадите таблицу вручную, «./manage.py syncdb» не будет с ней связываться.
  2. Если вы не хотите делать это вручную каждый раз при создании БД, поместите некоторый пользовательский SQL в appname/sql/modelname.sql для выполнения соответствующей команды ALTER TABLE.
  3. Создайте пользовательский тип поля и соответствующим образом определите метод db_type.

При любом из этих вариантов вы должны будете учитывать последствия для переносимости между базами данных. В варианте 2 вы можете использовать database-backend-specific пользовательский SQL, чтобы ваша инструкция ALTER TABLE выполнялась только в MySQL. В варианте 3 ваш метод db_type должен будет проверить механизм базы данных и установить тип столбца db на тип, который фактически существует в этой базе данных.

ОБНОВЛЕНИЕ: поскольку в Django 1.7 была добавлена ​​среда миграции, варианты 1 и 2 выше полностью устарели. Вариант 3 всегда был лучшим вариантом в любом случае. Новая версия вариантов 1/2 будет включать сложную пользовательскую миграцию с использованием SeparateDatabaseAndState, но на самом деле вам нужен вариант 3.

person Carl Meyer    schedule 29.08.2008

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:

Это еще один приятный и простой способ реализации перечислений, хотя на самом деле он не сохраняет перечисления в базе данных.

Однако он позволяет вам ссылаться на «метку» при каждом запросе или указании значений по умолчанию, в отличие от ответа с самым высоким рейтингом, где вы должны использовать «значение» (которое может быть числом).

person keithxm23    schedule 26.10.2012

Установка 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    |                |
+--------+-----------------------------+------+-----+---------+----------------+
person David Cain    schedule 26.09.2013
comment
Очень полезный ответ! Но это не будет работать для PostgreSQL. Причина в том, что PostgreSQL ENUM не поддерживает значения по умолчанию. В PostgreSQL сначала нужно создать CREATE DOMAIN или CREATE TYPE. Ссылка 8.7. Перечисленные типы Я попробовал трюк @David, и он отлично работает с MySQL, но в PostgrSQL работа заканчивается ошибкой 'type "enum" does not exist LINE 1: ....tablename" ADD COLUMN "select_user" ENUM('B', ...'. - person Grijesh Chauhan; 24.01.2014

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

  1. Используйте Джанго 1.x
  2. Признайте, что ваше приложение будет работать только с некоторыми базами данных.
  3. Поразмышляйте над этой страницей документации:http://docs.djangoproject.com/en/dev/howto/custom-model-fields/#howto-custom-model-fields

Удачи!

person Charles Merriam    schedule 02.12.2008

В настоящее время есть два проекта github, основанные на их добавлении, хотя я не изучал, как именно они реализованы:

  1. Django-EnumField:
    Предоставляет поле модели перечисления Django (с использованием IntegerField) с многократно используемыми перечислениями и подтверждение перехода.
  2. Django-EnumFields:
    Этот пакет позволяет использовать настоящие перечисления Python (в стиле PEP435) с Джанго.

Я не думаю, что они используют типы перечислений БД, но они в работе для первого.

person Pureferret    schedule 09.02.2015

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")
        ]
person Cesar Canassa    schedule 22.09.2019

В верхней части вашего файла models.py добавьте эту строку после импорта:

    enum = lambda *l: [(s,_(s)) for s in l]
person Kenzo    schedule 03.03.2014

person    schedule
comment
Начиная с django 1.2, вам необходимо добавить второй параметр, соединение, в определение db_type. - person Hans Lawrenz; 11.09.2012
comment
Что тогда случилось с codecatelog? Локос, вроде, мог бы быть хорошей идеей.... Сейчас получаю 404 - даже для корневой страницы. - person Danny Staple; 28.11.2012