【问题标题】:Specifying a mySQL ENUM in a Django model在 Django 模型中指定 mySQL ENUM
【发布时间】:2010-09-06 11:50:23
【问题描述】:

如何在 Django 模型中指定和使用 ENUM?

【问题讨论】:

  • 史蒂夫,如果你的意思是使用 MySQL ENUM 类型,那么你就不走运了,据我所知 Django 不提供支持(该功能并非在所有支持的数据库中都可用姜戈)。 Paul 提供的答案有效,但它不会定义数据库中的类型。

标签: python mysql django django-models enums


【解决方案1】:

来自Django documentation

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

你在你的模型中定义了一个字符域:

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

如果你不喜欢有字母,你可以对整数字段做同样的事情 在你的数据库中。

在这种情况下,重写你的选择:

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

【讨论】:

  • 如果之前没有清理过,这不会阻止“假”值被保存,是吗?
  • @Strayer 是的,我想这仅对使用模型表单有用
  • 请注意,推荐的 Django 风格暗示字符应该是常量:docs.djangoproject.com/en/dev/internals/contributing/…
  • 正如@Carl Meyer 在他的回答中所说,这不会在数据库中创建一个 ENUM 列。它创建了一个 VARCHAR 或 INTEGER 列,因此它并没有真正回答问题。
  • 我可以在整数字段中添加选择功能吗? @fulmicoton
【解决方案2】:
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) )

【讨论】:

  • 从 django 1.2 开始,您需要向 db_type def 添加第二个参数 connection。
  • codecatelog 发生了什么? Lokos 之类的可能是个好主意....我现在得到 404 - 即使是根页面。
【解决方案3】:

使用choices 参数不会使用ENUM db 类型;它只会创建一个 VARCHAR 或 INTEGER,具体取决于您是否将 choices 与 CharField 或 IntegerField 一起使用。一般来说,这很好。如果在数据库级别使用 ENUM 类型对您很重要,您有三个选择:

  1. 使用“./manage.py sql appname”查看Django生成的SQL,手动修改为使用ENUM类型,自己运行。如果你先手动创建表,“./manage.py syncdb”就不会搞砸了。
  2. 如果您不想在每次生成数据库时手动执行此操作,请将一些自定义 SQL 放入 appname/sql/modelname.sql 以执行相应的 ALTER TABLE 命令。
  3. 创建一个custom field type 并适当地定义db_type 方法。

使用这些选项中的任何一个,您都有责任处理对跨数据库可移植性的影响。在选项 2 中,您可以使用 database-backend-specific custom SQL 来确保您的 ALTER TABLE 仅在 MySQL 上运行。在选项 3 中,您的 db_type 方法需要检查数据库引擎并将 db 列类型设置为该数据库中实际存在的类型。

更新:由于迁移框架是在 Django 1.7 中添加的,所以上面的选项 1 和 2 完全过时了。无论如何,选项 3 始终是最佳选择。选项 1/2 的新版本将涉及使用 SeparateDatabaseAndState 的复杂自定义迁移——但您确实需要选项 3。

【讨论】:

    【解决方案4】:

    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:
    

    这是实现枚举的另一种不错且简单的方法,尽管它并没有真正将枚举保存在数据库中。

    但是,它确实允许您在查询或指定默认值时引用“标签”,而不是必须使用“值”(可能是数字)的顶级答案。

    【讨论】:

      【解决方案5】:

      在字段上设置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。 Reff 8.7. Enumerated Types 我尝试了@David 的技巧,它在 MySQL 上运行良好,但在 PostgrSQL 中工作最终出现错误 'type "enum" does not exist LINE 1: ....tablename" ADD COLUMN "select_user" ENUM('B', ...'
      【解决方案6】:

      如果你真的想使用你的数据库 ENUM 类型:

      1. 使用 Django 1.x
      2. 认识到您的应用程序仅适用于某些数据库。
      3. 通过此文档页面拼图:http://docs.djangoproject.com/en/dev/howto/custom-model-fields/#howto-custom-model-fields

      祝你好运!

      【讨论】:

        【解决方案7】:

        目前有两个基于添加这些的 github 项目,尽管我还没有详细了解它们是如何实现的:

        1. Django-EnumField:
          提供具有可重用枚举和转换验证的枚举 Django 模型字段(使用 IntegerField)。
        2. Django-EnumFields:
          这个包让你可以在 Django 中使用真正的 Python(PEP435 风格)枚举。

        我认为两者都不使用 DB 枚举类型,但它们是 in the works 第一个。

        【讨论】:

          【解决方案8】:

          Django 3.0 内置了对枚举的支持

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

          现在,请注意它不会在数据库级别强制选择这是 Python 唯一的构造。如果您还想在数据库中强制执行这些值,您可以将其与数据库约束结合起来:

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

          【讨论】:

            【解决方案9】:

            models.py 文件的顶部,在导入后添加这一行:

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

            【讨论】:

              猜你喜欢
              • 2013-11-22
              • 1970-01-01
              • 2012-08-13
              • 2010-11-30
              • 2015-11-19
              • 1970-01-01
              • 2012-02-19
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多