developer tip

Django 모델에서 mySQL ENUM 지정

optionbox 2020. 9. 5. 09:44
반응형

Django 모델에서 mySQL ENUM 지정


Django 모델에서 ENUM을 지정하고 사용하려면 어떻게해야합니까?


로부터 장고 문서 :

MAYBECHOICE = (
    ('y', 'Yes'),
    ('n', 'No'),
    ('u', 'Unknown'),
)

그리고 모델에 charfield를 정의합니다.

married = models.CharField(max_length=1, choices=MAYBECHOICE)

db에 문자를 포함하지 않으려면 정수 필드에 대해서도 동일하게 수행 할 수 있습니다.

이 경우 선택 사항을 다시 작성하십시오.

MAYBECHOICE = (
    (0, 'Yes'),
    (1, 'No'),
    (2, 'Unknown'),
)

from django.db import models

class EnumField(models.Field):
    """
    A field class that maps to MySQL's ENUM type.

    Usage:

    class Card(models.Model):
        suit = EnumField(values=('Clubs', 'Diamonds', 'Spades', 'Hearts'))

    c = Card()
    c.suit = 'Clubs'
    c.save()
    """
    def __init__(self, *args, **kwargs):
        self.values = kwargs.pop('values')
        kwargs['choices'] = [(v, v) for v in self.values]
        kwargs['default'] = self.values[0]
        super(EnumField, self).__init__(*args, **kwargs)

    def db_type(self):
        return "enum({0})".format( ','.join("'%s'" % v for v in self.values) )

은 Using choices를 상기 ENUM DB 유형을 사용하지 않습니다 매개 변수를; choicesCharField 또는 IntegerField와 함께 사용하는지 여부에 따라 VARCHAR 또는 INTEGER를 만듭니다. 일반적으로 이것은 괜찮습니다. ENUM 유형이 데이터베이스 수준에서 사용되는 것이 중요한 경우 다음 세 가지 옵션이 있습니다.

  1. "./manage.py sql appname"을 사용하여 Django가 생성하는 SQL을 확인하고 수동으로 수정하여 ENUM 유형을 사용하고 직접 실행합니다. 먼저 수동으로 테이블을 생성하면 "./manage.py syncdb"가 엉망이되지 않습니다.
  2. DB를 생성 할 때마다이를 수동으로 수행하지 않으려면 appname / sql / modelname.sql에 사용자 지정 SQL을 넣어 적절한 ALTER TABLE 명령을 수행하십시오.
  3. 사용자 정의 필드 유형을 작성 하고 db_type 메소드를 적절하게 정의하십시오.

With any of these options, it would be your responsibility to deal with the implications for cross-database portability. In option 2, you could use database-backend-specific custom SQL to ensure your ALTER TABLE is only run on MySQL. In option 3, your db_type method would need to check the database engine and set the db column type to a type that actually exists in that database.

UPDATE: Since the migrations framework was added in Django 1.7, options 1 and 2 above are entirely obsolete. Option 3 was always the best option anyway. The new version of options 1/2 would involve a complex custom migration using SeparateDatabaseAndState -- but really you want option 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:

This is another nice and easy way of implementing enums although it doesn't really save enums in the database.

However it does allow you to reference the 'label' whenever querying or specifying defaults as opposed to the top-rated answer where you have to use the 'value' (which may be a number).


Setting choices on the field will allow some validation on the Django end, but it won't define any form of an enumerated type on the database end.

As others have mentioned, the solution is to specify db_type on a custom field.

If you're using a SQL backend (e.g. MySQL), you can do this like so:

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)

Run syncdb, and inspect your table to see that the ENUM was created properly.

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    |                |
+--------+-----------------------------+------+-----+---------+----------------+

If you really want to use your databases ENUM type:

  1. Use Django 1.x
  2. Recognize your application will only work on some databases.
  3. Puzzle through this documentation page:http://docs.djangoproject.com/en/dev/howto/custom-model-fields/#howto-custom-model-fields

Good luck!


There're currently two github projects based on adding these, though I've not looked into exactly how they're implemented:

  1. Django-EnumField:
    Provides an enumeration Django model field (using IntegerField) with reusable enums and transition validation.
  2. Django-EnumFields:
    This package lets you use real Python (PEP435-style) enums with Django.

I don't think either use DB enum types, but they are in the works for first one.


Django 3.0 has built-in support for Enums

From the documentation:

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,
    )

Now, be aware that it does not enforce the choices at a database level this is Python only construct. If you want to also enforce those value at the database you could combine that with database constraints:

class Student(models.Model):
    ...

    class Meta:
        constraints = [
            CheckConstraint(
                check=Q(year_in_school__in=YearInSchool.values),
                name="valid_year_in_school")
        ]

A the top of your models.py file, add this line after you do your imports:

    enum = lambda *l: [(s,_(s)) for s in l]

참고URL : https://stackoverflow.com/questions/21454/specifying-a-mysql-enum-in-a-django-model

반응형