在Django模型中指定一个mySQL ENUM
我如何去在Django模型中指定和使用ENUM?
从Django文档 :
MAYBECHOICE = ( ('y', 'Yes'), ('n', 'No'), ('u', 'Unknown'), )
你在你的模型中定义一个charfield:
married = models.CharField(max_length=1, choices=MAYBECHOICE)
如果你不喜欢你的数据库中有字母,你可以用整数字段来做同样的事情。
在这种情况下,重写你的select:
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) )
使用choices
参数不会使用ENUM数据库types; 它只会创build一个VARCHAR或INTEGER,这取决于您是使用CharField还是IntegerField来使用choices
。 一般来说,这很好。 如果在数据库级使用ENUMtypes对您很重要,您有三种select:
- 使用“./manage.py sql appname”来查看Django生成的SQL,手动修改它以使用ENUMtypes,并自己运行它。 如果您先手动创build表格,则“./manage.py syncdb”不会混淆。
- 如果您不希望每次生成数据库时手动执行此操作,请将一些自定义SQL放在appname / sql / modelname.sql中以执行相应的ALTER TABLE命令。
- 创build一个自定义字段types并适当地定义db_type方法。
有了这些选项,处理交叉数据库可移植性的影响将是您的责任。 在选项2中,您可以使用数据库后端特定的自定义SQL来确保您的ALTER TABLE仅在MySQL上运行。 在选项3中,db_type方法需要检查数据库引擎,并将db列的types设置为该数据库中实际存在的types。
更新 :由于在Django 1.7中添加了迁移框架,上面的选项1和2完全过时了。 选项3始终是最好的select。 新版本的选项1/2将涉及使用SeparateDatabaseAndState
复杂的自定义迁移 – 但实际上你想要选项3。
在字段上设置choices
将允许在Django结束一些validation,但是它不会在数据库端定义任何forms的枚举types。
正如其他人所提到的,解决scheme是在自定义字段上指定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
是否正确创build。
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 | | +--------+-----------------------------+------+-----+---------+----------------+
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:
这是实现枚举的另一个不错的简单方法,虽然它并不真正保存数据库中的枚举。
但是,每当查询或指定默认值时,它都允许您引用“标签”,而不是必须使用“值”(可能是数字)的顶级答案。
如果你真的想使用你的数据库ENUMtypes:
- 使用Django 1.x
- 认识到你的应用程序只能在一些数据库上工作。
- 通过这个文档页面拼图: http : //docs.djangoproject.com/en/dev/howto/custom-model-fields/#howto-custom-model-fields
祝你好运!
目前有两个基于添加这些github项目,但我没有看到他们是如何实现的:
- Django的EnumField :
提供枚举Django模型字段(使用IntegerField)和可重用枚举和转换validation。 - Django-EnumFields :
这个包让你使用真正的Python(PEP435式)枚举与Django。
我不认为要么使用数据库枚举types,但他们正在为第一个作品 。
在您的models.py文件的顶部,在进行导入之后添加以下行:
enum = lambda *l: [(s,_(s)) for s in l]