Django loaddata 对于 unicode 失败

Posted

技术标签:

【中文标题】Django loaddata 对于 unicode 失败【英文标题】:Django loaddata fails for unicode 【发布时间】:2018-11-28 17:08:06 【问题描述】:

这是模型:

class ListItem(models.Model):
   # id -- PK
   dateCreated = models.DateTimeField(auto_now_add=True)
   dateModified = models.DateTimeField(auto_now_add=True)
   listId = models.IntegerField(null=False)  # required FK => List
   itemId = models.CharField(max_length=8, null=False)
   description = models.CharField(max_length=1024)
   notes = models.CharField(max_length=2048)

   class Meta:
      ordering = ('itemId',)

这是来自夹具文件(json)的违规项目:

    
    "model": "myproject.ListItem",
    "pk": 721,
    "fields": 
        "listId": 26,
        "itemId": "A3",
        "description": "Statystyka Książek Papierowych, Mówionych I Elektronicznych",
        "notes": "Polish Statistical Book and E-book Classification",
        "dateCreated": "2018-05-14 22:05:25",
        "dateModified": "2018-05-14 22:05:25"
    
, ...

这是我用来尝试加载数据的命令:

python3 manage.py loaddata listItems.json

导致以下错误:

django.db.utils.OperationalError: Problem installing fixture \
'/Users/sloughin/dev/myproject/fixtures/listItems.json': \
Could not load myproject.ListItem(pk=721): \
(1366, "Incorrect string value: '\\xC4\\x85\\xC5\\xBCek...' \
for column 'description' at row 1")

我是否应该在模型中使用一些标志来表明我希望该字段包含 unicode 数据?

这是针对 Ubuntu 16.04 上的 mysql 数据库运行的,我在 iMac (OSX 10.13.5) 上运行 python 3.6.2。我所有其他 loaddata 操作都运行良好。

【问题讨论】:

【参考方案1】:

好的,我想我想通了。 MySQL 数据库 VARCHAR 字段默认为 utf8,它支持大多数(但不是全部)unicode 字符。这就是为什么有些记录可以,而有些则不行。您必须使用 utf8mb4 而不是 utf8。这增加了数据库字段的宽度,因为它们现在每个字符使用 4 个字节而不是 3 个字节(因此对于某些 DBMS,您可能还需要将模型中字段的宽度扩大 4/3 倍)虽然我' m 告诉 MySQL 字段是用字符指定的,而不是字节。

但是,我们需要使用一个新的迁移文件,因此我要将其中一个字段更改为长一个字符。

class ListItem(models.Model):
      # id -- PK
      dateCreated = models.DateTimeField(auto_now_add=True)
      dateModified = models.DateTimeField(auto_now_add=True)
      listId = models.IntegerField(null=False)  # required FK => List
      itemId = models.CharField(max_length=8, null=False)
      description = models.CharField(max_length=1025)  # was 1024
      notes = models.CharField(max_length=2048)  

我重发python3 manage.py makemigrations

接下来,我进入迁移文件夹并编辑了新的迁移文件(我的名为 002_auto_20180619_1849.py),其中包含定义了 operationsclass Migrations:。您将看到使字段更宽的 AlterField 操作,但之后您可以附加额外的 SQL 操作,如下所示:

migrations.AlterField(...),
migrations.RunSQL(
    'alter table myproject_listitem CONVERT TO CHARACTER SET 
     utf8mb4 COLLATE utf8mb4_unicode_ci;'),
migrations.RunSQL(
    'alter table myproject_listitem CONVERT TO CHARACTER SET 
     utf8mb4 COLLATE utf8mb4_unicode_ci;'),
migrations.RunSQL(
    'alter table myproject_listitem change description description 
     VARCHAR(1366) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'),
migrations.RunSQL(
    'alter table myproject_listitem change notes notes 
     VARCHAR(2731) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;'),

在这里,我将每个 SQL 语句分成两行,以便您阅读。如果您可以直接访问数据库,您也可以在 mysql 会话中执行此操作,但我想知道如何在迁移过程中执行此操作。

在运行此迁移之前,请检查以确保您已安装 sqlparse。如果没有,那么 pip3 install sqlparse 或将其作为要求包含在您的虚拟环境或 docker 容器中。那么:

python3 manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, myproject, sessions
Running migrations:
  Applying myproject.0002_auto_20180619_1849... OK

之后,我可以运行python3 manage.py loaddata listItems.json,一切正常!

【讨论】:

这些长度是“字节”吗?不在 MySQL 中——它们在“字符”中,所以 4/3 不是必需的。 (但它是“无害的”。)

以上是关于Django loaddata 对于 unicode 失败的主要内容,如果未能解决你的问题,请参考以下文章

以编程方式使用 Django 的 loaddata

Django 夹具未加载 loaddata

Django 1.4:如何忽略 loaddata 中的字段、模型

Django dumpdata 和 loaddata 不适用于多对多中间模型

由于夹具错误,Django datadump 和 loaddata 无法正常工作

在 Django 1.7 迁移中调用 loaddata 会抛出“‘字段列表’中的未知列‘[字段]’”