【问题标题】:Django Migration Error with MySQL: BLOB/TEXT column 'id' used in key specification without a key length"MySQL 的 Django 迁移错误:密钥规范中使用的 BLOB/TEXT 列 'id' 没有密钥长度"
【发布时间】:2016-11-17 03:12:36
【问题描述】:

我们有 Django 模型,使用二进制字段作为 ID。

# Create your models here.
class Company(models.Model):
    id = models.BinaryField(max_length=16, primary_key=True)
    name = models.CharField(max_length=12)

    class Meta:
        db_table = "company"

我们使用 MySQL 数据库,迁移时出现错误。

  File "/home/cuongtran/Downloads/sample/venv/lib/python3.5/site-packages/MySQLdb/connections.py", line 270, in query
    _mysql.connection.query(self, query)
django.db.utils.OperationalError: (1170, "BLOB/TEXT column 'id' used in key specification without a key length")

你有什么解决办法吗?我们需要使用 MySQL 并希望使用二进制字段作为 ID。

谢谢!

【问题讨论】:

  • 只是我学到的一个建议:不要使用二进制主键。与他们一起工作非常不舒服。

标签: python mysql django


【解决方案1】:

我认为你无法做到这一点。根据 Django 文档,二进制字段的使用似乎是 discouraged

存储原始二进制数据的字段。它只支持字节分配。 请注意,此字段的功能有限。例如,它是 无法根据 BinaryField 值过滤查询集。也是 不能在 ModelForm 中包含 BinaryField。

滥用 BinaryField

虽然您可能会考虑将文件存储在数据库中,但请考虑 在 99% 的情况下,它是糟糕的设计。该字段不是 替换正确的静态文件处理。

基于Django bug,在二进制字段上实现unique value 限制很可能是不可能的。此错误标记为wont-fix。我说很可能是不可能的,因为我没有找到证据来确认二进制字段存储为BLOB field,但错误确实暗示了这一点。

说明

当我使用这样的字段时:
text = models.TextField(maxlength=2048, unique=True)
当管理应用程序创建表时,它会导致以下 sql 错误
_mysql_exceptions.OperationalError: (1170, "BLOB/TEXT column 'text' used in key specification without a key length")
经过一番调查,事实证明mysql 拒绝对列使用唯一性,除非它仅用于文本字段的索引部分:

CREATE TABLE `quotes` ( \`id\` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `text` longtext NOT NULL , \`submitTS\` datetime NOT NULL, `submitIP` char(15) NOT NULL, `approved` bool NOT NULL, unique (text(1000)));

当然 1000 只是我选择的任意数字,它恰好是我的数据库允许的最大值。不完全确定如何解决此问题,但我认为值得一提。

【讨论】:

    【解决方案2】:

    MySQL 将 BLOB/TEXT 列的主键限制为前 N 个字符,当您使用 Django 的 makemigrations 命令生成迁移文件时,Django 中的 BinaryField 映射到 MySQL 中的 BLOB 列 longblob 不指定密钥长度

    这意味着你的 Django 模型定义:

    class Company(models.Model):
        id = models.BinaryField(max_length=16, primary_key=True)
        name = models.CharField(max_length=12)
        class Meta:
            db_table = "company"
    

    将转换为导致此错误的SQL表达式(您可以通过sqlmigrate命令查看详细的SQL表达式):

    CREATE TABLE `company` (`id` longblob NOT NULL PRIMARY KEY,
      `name` varchar(12) NOT NULL);
    

    虽然 MySQL 的正确 SQL 表达式应该是这样的:

    CREATE TABLE `company` (`id` longblob NOT NULL,
      `name` varchar(12) NOT NULL);
    ALTER TABLE  `company` ADD PRIMARY KEY (id(16));
    

    其中PRIMARY KEY (id(16)) 来自您在BLOB 列中的id 长度,用于构造表的主键索引。

    因此,最简单的解决方案如已接受的答案中所述——避免在 Django 中将 BinaryField 作为主键,或者如果您确实需要 BinaryField(BLOB 列),您可以手动将原始 SQL 脚本添加到迁移文件中是主键,并且您确定 id 字段不会超出特定大小(在您的情况下为 16 个字节)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-11-22
      • 1970-01-01
      • 1970-01-01
      • 2019-01-05
      • 2010-12-22
      相关资源
      最近更新 更多