2008-08-21 14 views
78

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

+3

MySQL ENUMタイプを使用していた場合、Djangoはサポートしていません(この機能はDjangoでサポートされているすべてのDBで利用できません)。ポールが提供した答えは動作しますが、**はDBの型を定義しません**。 – dguaraglia

答えて

90
Django documentationから

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

そして、あなたはあなたのモデルでCharFieldです定義:あなたが手紙 を持って好きではない場合は、整数フィールドで同じことを行うことができます

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

をあなたのデータベース。その場合

、あなたの選択肢を書き換える:

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

これで、前にクリーニングされていないと「偽」値が保存されるのを防ぐことはできませんか? – Strayer

+0

@Strayerはい、これはモデルフォームを使用する場合にのみ便利ですね – Acute

+0

推奨されるDjangoスタイルは、文字が定数であることを意味します。/coding-style /#model-style –

29

choicesパラメータを使用するには、ENUMのDBタイプを使用することはありません。 CharFieldまたはIntegerFieldでchoicesを使用するかどうかによって、VARCHARまたはINTEGERが作成されます。一般的に、これは問題ありません。

  1. 使用「./manage.pyのSQL appnameの」手動で使用するように変更し、SQL Djangoが生成参照するには:それはENUM型はデータベースレベルで使用されていることを、あなたにとって重要な場合は、次の3つのオプションがありENUMの種類、それを自分で実行します。最初に手動でテーブルを作成すると、 "./manage.py syncdb"はそれを混乱させません。
  2. DBを生成するたびに手動で実行したくない場合は、appname/sql/modelname.sqlにカスタムSQLを入れて、適切なALTER TABLEコマンドを実行します。
  3. custom field typeを作成し、db_typeメソッドを適切に定義します。

これらのオプションのいずれかを使用すると、データベース間の移植性への影響に対処することはお客様の責任です。オプション2では、database-backend-specific custom SQLを使用して、ALTER TABLEがMySQL上でのみ実行されるようにすることができます。オプション3では、db_typeメソッドはデータベースエンジンをチェックし、実際にそのデータベースに存在する型にdbカラム型を設定する必要があります。

UPDATE:移行フレームワークはDjango 1.7で追加されているため、上記のオプション1と2はまったく時代遅れです。とにかくオプション3はいつも最高の選択肢でした。オプション1/2の新バージョンでは、SeparateDatabaseAndStateを使用して複雑なカスタム移行を伴うだろう - しかし、本当にあなたがしたいオプション

34
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)) 
+1

ありがとうございます。 http://www.codecatalog.net/407/ – luqui

+2

django 1.2では、db_type defに2番目のパラメータconnectionを追加する必要があります。 –

+2

codecatelogに何が起こったのですか? Lokosのように良いアイデアだったかもしれません....私は404を得ました - ルートページでさえ。 –

8

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: 

これは本当に、データベース内の列挙型は保存されませんが列挙型を実装する他素晴らしく、簡単な方法です。

しかし、「値」(数字でもよい)を使用する必要がある最高評価の回答ではなく、クエリやデフォルトを指定するたびに「ラベル」を参照することができます。

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

非常に参考になった!しかし、これはPostgreSQLでは機能しません。理由はPostgreSQLですENUMはデフォルトをサポートしていません。 PostgreSQLでは、まずCREATE DOMAINまたはCREATE TYPEを作成する必要があります。 Reff [8.7。列挙型](http://www.postgresql.org/docs/9.2/static/datatype-enum.html)@ Davidのトリックを試してみましたが、それはMySQLで正常に動作していますが、PostgrSQLの作業ではエラーが発生します。タイプ "enum"は存在しません LINE 1:.... tablename "列を追加してください" select_user "ENUM( 'B'、... ''。 –

-2

Aあなたのmodels.pyファイルの先頭、次の行を追加します。

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

どうしますか? – Pureferret

3

私はいませんでしたけれども、これらを追加することに基づいて、2つのgithubのプロジェクトが、現在はそこにいます

  1. Django-EnumFieldを::
    は、再利用可能な列挙型と遷移VALIDAで(IntegerFieldとを使用して)列挙Djangoのモデルフィールドを提供し、彼らが実装されている正確にどのように見えました。
  2. Django-EnumFields
    このパッケージでは、実際のP​​ython(PEP435スタイル)の列挙型をDjangoで使用できます。

私はDB enumタイプを使用しないと思いますが、最初はin the worksです。