3

我想做什么?

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

错误详情

_mysql.connection.query(self, query) django.db.utils.ProgrammingError: (1064, "您的 SQL 语法有错误;请查看与您的 MariaDB 服务器版本相对应的手册,以了解在 'NOT 附近使用的正确语法NULL, created_atdatetime(6) NOT NULL, user_idbigint 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 个参数以传递逗号分隔值和默认值。

4

3 回答 3

3

数据类型应该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自己。

于 2021-09-30T03:07:21.737 回答
-1

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

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

Enumeration types由于某种原因,文档中推荐的语法是否对您不起作用?

于 2021-09-29T19:39:45.380 回答
-1

对赏金问题的回应

设置默认值:在 EnumField 中添加默认参数。下面的示例我已将 enumTokenTypes Registration 设置为其默认值。使用示例查看Django 文档以实现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)    
于 2021-10-09T14:46:37.320 回答