【问题标题】:Issue while trying to set enum data type in MySQL database尝试在 mysql 数据库中设置枚举数据类型时出现问题
【发布时间】:2021-11-20 17:29:26
【问题描述】:

我想做什么?

Django 不支持在 mysql 数据库中设置枚举数据类型。使用下面的代码,我尝试设置枚举数据类型。

错误详情

_mysql.connection.query(self, query) django.db.utils.ProgrammingError: (1064, "You have an error in your SQL syntax; check the manual that 对应于您的 MariaDB 服务器版本,以便使用正确的语法 'NOT NULL, created_at datetime(6) NOT NULL, user_id bigint 附近 NOT NULL)' 在第 1 行")

我错过了什么吗?

具有所有选择的枚举类

class enumTokenTypes(models.TextChoices):
    Registration = "Registration"
    ForgotPassword = "Forgot Password"

模型中的用户令牌类

class tblusertokens(models.Model):
    token_id = AutoField(primary_key=True)
    token_type = EnumField(max_length=20, choices=enumTokenTypes.choices)
    created_at = DateTimeField(auto_now_add=True, blank=True)
    user = ForeignKey(tblusers, on_delete = models.CASCADE)    

迁移中的用户令牌创建模型

class EnumField(CharField):
    def db_type(self, connection):
        return "enum"


migrations.CreateModel(
    name='tblusertokens',
    fields=[
        ('token_id', models.AutoField(primary_key=True, serialize=False)),
        ('token_type', clientauth.models.EnumField(choices=[('Registration', 'Registration'), ('Forgot Password', 'Forgotpassword')], max_length=20)),
        ('created_at', models.DateTimeField(auto_now_add=True)),
        ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='clientauth.tblusers')),
    ],
)

赏金问题

为函数设置 2 个参数以传递逗号分隔值和默认值。

【问题讨论】:

    标签: python python-3.9 django-3.2


    【解决方案1】:

    对赏金问题的回应

    用于设置默认值:在 EnumField 中添加默认参数。下面的示例我已将 enumTokenTypes Registration 设置为其默认值。通过示例查看 Django documentation 以获取 enum 实现

         class tblusertokens(models.Model):
              token_id = AutoField(primary_key=True)
              token_type = EnumField(max_length=20, choices=enumTokenTypes.choices, default=enumTokenTypes.Registration )
              created_at = DateTimeField(auto_now_add=True, blank=True)
              user = ForeignKey(tblusers, on_delete = models.CASCADE)    
    

    【讨论】:

      【解决方案2】:

      数据类型应该是enum('Registration', 'Forgot Password') 而不仅仅是enum

      class EnumField(CharField):
      
          def db_type(self, connection):
              if connection.vendor == 'mysql':
                  return 'enum({0})'.format(','.join("'%s'" % value for value, label in self.choices))
              return super().db_type(connection)
      

      参考:https://dev.mysql.com/doc/refman/8.0/en/enum.html

      数据库默认

      虽然上面的 MySQL 8.0 文档中没有明确提及,但您也可以指定数据库默认值。

      class EnumField(CharField):
      
          def __init__(self,  *args, **kwargs):
              self.db_default = kwargs.pop('db_default', None)
              super().__init__(*args, **kwargs)
      
          def db_type(self, connection):
              if connection.vendor == 'mysql':
                  if self.db_default is not None:
                      return "enum({0}) DEFAULT '{1}'".format(','.join("'%s'" % value for value, label in self.choices), self.db_default)
                  return 'enum({0})'.format(','.join("'%s'" % value for value, label in self.choices))
              return super().db_type(connection)
      
          def deconstruct(self):
              name, path, args, kwargs = super().deconstruct()
              if self.db_default:
                  kwargs['db_default'] = self.db_default
              return name, path, args, kwargs
      

      用法:

      token_type = EnumField(max_length=20, choices=enumTokenTypes.choices, db_default=enumTokenTypes.ForgotPassword)
      

      关于deconstruct()方法

      来自https://docs.djangoproject.com/en/3.2/howto/custom-model-fields/#field-deconstruction

      编写__init__() 方法的对应点是编写deconstruct() 方法。它在模型迁移期间用于告诉 Django 如何获取新字段的实例并将其简化为序列化形式 - 特别是传递给 __init__() 以重新创建它的参数。

      如果您添加一个新的关键字参数,您需要自己在deconstruct() 中编写代码,将其值放入kwargs

      【讨论】:

        【解决方案3】:

        您可以打印出该迁移的 sql 以查看具体出了什么问题,但将 db_type 定义为返回 "enum" 绝对不是正确的处理方法。

            ('token_type', CharField(choices=enumTokenTypes.choices, max_length=22)),
        

        Enumeration types 上的文档中推荐的语法是否因某种原因不适合您?

        【讨论】:

        • 我想显式设置列数据类型 - enum 但 django 将列类型 varchar 设置为数据类型是一种混淆。
        • 创建enum 列或varchar 列的语法之间存在更多差异,而不仅仅是关键字。如果您想实现这一点,您将无法为其子类化 CHAR - 实际上,在数据库上强制执行的枚举字段并不比在应用程序中强制执行的枚举字段好多少(除非您从外部访问数据库应用程序)。
        猜你喜欢
        • 2021-10-13
        • 2021-12-28
        • 1970-01-01
        • 1970-01-01
        • 2017-05-17
        • 2022-01-28
        • 1970-01-01
        • 2021-06-30
        • 1970-01-01
        相关资源
        最近更新 更多