DjangoモデルでのmySQL ENUMの指定


92

DjangoモデルでENUMを指定して使用するにはどうすればよいですか?


4
スティーブ、MySQL ENUMタイプを使用するつもりなら、Djangoがそのサポートを提供していないことを知っている限り(DjangoがサポートするすべてのDBでこの機能を利用できるわけではありません)、運が悪いです。Paulによって提供された答えは機能しますが、DBのタイプを定義しません
dguaraglia 2008

回答:


108

Djangoのドキュメントから:

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

そして、あなたはあなたのモデルでcharfieldを定義します:

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

dbに文字を入れたくない場合は、整数フィールドでも同じことができます。

その場合は、選択内容を書き直してください。

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

8
これは、以前にクリーンアップされていない場合に「false」値が保存されるのを防ぎませんか?
Strayer

はい@Strayer、私はこれが唯一のモデルのフォームを使用するために有用であると思います
急性

:Djangoのスタイルを推奨注文字は定数でなければならないことを意味しdocs.djangoproject.com/en/dev/internals/contributing/...
マイケルScheper

11
@Carl Meyerが彼の回答で述べたように、これはデータベースにENUM列を作成しません。VARCHAR列またはINTEGER列が作成されるため、実際には質問の答えにはなりません。
アリエル

整数フィールドで選択肢機能を追加できますか?@fulmicoton
Ilyas karim

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

2
django 1.2以降では、db_type defに2つ目のパラメーターconnectionを追加する必要があります。
ハンス・ローレンツ2012

2
その後、codecatelogに何が起こりましたか?それのようなLokosは良い考えだったかもしれません...私は今、404を取得します-ルートページについてもです。
ダニーステープル

33

使用するchoicesパラメータは、ENUMのDBタイプを使用することはありません。choicesCharFieldとIntegerFieldのどちらを使用するかに応じて、VARCHARまたはINTEGERを作成するだけです。通常、これで十分です。ENUMタイプをデータベースレベルで使用することが重要な場合は、3つのオプションがあります。

  1. 「./manage.py sql appname」を使用して、SQL Djangoが生成するSQLを確認し、ENUMタイプを使用するように手動で変更して、自分で実行します。最初に手動でテーブルを作成した場合、「./ manage.py syncdb」はテーブルを台無しにしません。
  2. DBを生成するたびに手動でこれを実行したくない場合は、appname / sql / modelname.sqlにカスタムSQLを配置して、適切なALTER TABLEコマンドを実行します。
  3. カスタムフィールドタイプを作成し、db_typeメソッドを適切に定義します。

これらのオプションのいずれかを使用する場合、データベース間の移植性の影響に対処するのはユーザーの責任です。オプション2では、データベースバックエンド固有のカスタムSQLを使用して、ALTER TABLEがMySQLでのみ実行されるようにすることができます。オプション3では、db_typeメソッドでデータベースエンジンを確認し、dbカラムタイプをそのデータベースに実際に存在するタイプに設定する必要があります。

更新:Django 1.7で移行フレームワークが追加されたため、上記のオプション1と2は完全に廃止されました。とにかく、オプション3は常に最良のオプションでした。新しいバージョンのオプション1/2には、を使用した複雑なカスタム移行が含まれますSeparateDatabaseAndStateが、実際にはオプション3が必要です。


10

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:

これは、列挙を実際にデータベースに保存するわけではありませんが、列挙を実装するもう1つの優れた簡単な方法です。

ただし、「値」(数値の場合もあります)を使用する必要がある最高評価の回答とは対照的に、デフォルトを照会または指定するときはいつでも「ラベル」を参照できます。


9

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

とても参考になります。しかし、これは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', ...'
Grijesh Chauhan 2014


3

現在、これらの追加に基づいて2つのgithubプロジェクトがありますが、それらがどのように実装されているかは詳しく調べていません。

  1. Django-EnumField
    再利用可能な列挙型と遷移検証を備えた列挙型Djangoモデルフィールド(IntegerFieldを使用)を提供します。
  2. Django-EnumFields
    このパッケージを使用すると、実際のP​​ython(PEP435スタイル)列挙型をDjangoで使用できます。

私はどちらもDB列挙型を使用することはないと思いますが、それらは最初のもののための作業中です。


1

Django 3.0にはEnumのサポートが組み込まれています

ドキュメントから:

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")
        ]

-2

models.pyファイルの先頭に、インポートを行った後に次の行を追加します。

    enum = lambda *l: [(s,_(s)) for s in l]
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.