【问题标题】:How to set charset to utf8mb4 in flask-sqlalchemy in order to avoid pymysql.err.DataError: (1366, "Incorrect string value:如何在 flask-sqlalchemy 中将 charset 设置为 utf8mb4 以避免 pymysql.err.DataError: (1366, "Incorrect string value:
【发布时间】:2021-11-10 20:00:32
【问题描述】:

有谁知道如何使用 sqlalchemy 或专门使用 flask_sqlalchemy 设置 MySQL 表列来保存 utf8mb4 字符集?此处描述了连接https://docs.sqlalchemy.org/en/14/dialects/mysql.html,但我不知道如何更改表属性以使烧瓶迁移正确创建表。

我看到的问题是某些电子邮件内容(我正在尝试保存)可能有 4 字节 Unicode,并且无法插入到表中并出现此错误

 (pymysql.err.DataError) (1366, "Incorrect string value: '\\xEF\\xBB\\xBF\\x0D\\x0A>...' for column `test`.`email`.`content_text` at row 1")

对应于email中的这个字符串sn-p

>=20
> =EF=BB=BF
>=20

并且可以像这样被复制为测试,如果发送到数据库将重新创建错误:

print(f'bad chr >> {chr(65279)} <<')

要使表能够存储此字符,您可以使用 alter 这不是很好,因为它的 post 声明。

ALTER TABLE email
  DEFAULT CHARACTER SET utf8mb4,
  MODIFY content_text MEDIUMTEXT
    CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL

所以我想在表的声明中这样做,但无法弄清楚这是如何使用烧瓶 orm 接口完成的。

这是一个重新创建问题的测试脚本(在测试数据库存在并且测试用户已授予所有权限以创建表之后)

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from sqlalchemy.dialects.mysql import MEDIUMTEXT

app = Flask(__name__)

app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://test:test@localhost/test?charset=utf8mb4' 



db = SQLAlchemy(app)
Migrate(app,db)

class Email(db.Model):
    __tablename__ = 'email'
    id = db.Column(db.Integer, primary_key=True)
    content_text = db.Column(MEDIUMTEXT)


@app.cli.command("test_schema")
def test_schema():

    for i, row in  enumerate(db.session.execute('desc email'), 1):
        [print(f'row {i} {row._fields[y]:<10s} : {str(col):<20s}') for y,col in enumerate(row)]
        print()
        
        
@app.cli.command("test_problem")
def test_problem():
 
    new_mail = Email( content_text = f'bad chr >> {chr(65279)} <<' )
    db.session.add( new_mail )
    db.session.commit()
        

@app.cli.command("drop")
def drop():
    db.session.execute('drop table email')

多次调用它来创建架构,然后用我添加的命令行函数测试问题。

# creates the table provided test db is created and user has grant all set to that test db
FLASK_APP=test_schema.py flask db init
FLASK_APP=test_schema.py flask db migrate
FLASK_APP=test_schema.py flask db upgrade

# test the problem
FLASK_APP=test_schema.py flask test_problem

所以我希望能够在不更改数据库的情况下定义 content_text = db.Column(MEDIUMTEXT)

这些是任何ALTER TABLE 之前的数据库对象:

> SHOW CREATE DATABASE test;
CREATE DATABASE 'test' /*!40100 DEFAULT CHARACTER SET latin1 */  

> SHOW CREATE TABLE email;

CREATE TABLE 'email' (
   'id' int(11) NOT NULL AUTO_INCREMENT,
   'content_text' mediumtext DEFAULT NULL,
   PRIMARY KEY ('id') 
) ENGINE=InnoDB DEFAULT CHARSET=latin1

【问题讨论】:

  • 所以如果我理解正确,您想知道如何在创建表后自动使用正确的字符集创建列,而不必执行ALTER TABLE?使用 charset utf8mb4 创建数据库是否可以接受?
  • 您的意思是所有表都默认为 utf8mb4 ?更改数据库会将应用程序的特定责任推给数据库管理员,并且出于其他原因您可能不希望在所有表上都这样做。所以我个人认为解决方案应该在表的声明中。
  • 我搜索了方言的 sqlalchemy 处理,我发现这个工作 content_text = db.Column(MEDIUMTEXT(unicode=True)) 不确定它是否有任何副作用。

标签: python mysql unicode sqlalchemy flask-sqlalchemy


【解决方案1】:

这是一个 BOM:http://mysql.rjweb.org/doc.php/charcoll#bom_byte_order_mark

要么删除文件的前 3 个字节,要么让构建该文件的程序不包含 BOM。

你在连接字符串中指定了什么编码?

有问题的列是什么编码(在 MySQL 中)?看起来它是latin1,它不知道如何处理 UTF-8 BOM。你有重音字母吗?如果没有,请切换到utf8mb4

在 about 链接中向前搜索以找到我关于“SQLAlchemy”的笔记。

【讨论】:

  • 修改原始数据缓冲区中的字节似乎是个坏主意。这是您无法控制内容的电子邮件数据。问题是如何让 SQL Alchemy 在 MySql 中存储 utf8mb4 格式。特别是字符代码65279 持续存在并重新出现。
【解决方案2】:

啊,我被文档https://docs.sqlalchemy.org/en/14/dialects/mysql.html#mysql-data-types弄糊涂了

这些是实现类型的类。 MEDIUMTEXT 的文档字符串的实现表明它接受可以传递的参数。这是呈现的文档字符串 https://docs.sqlalchemy.org/en/14/dialects/mysql.html#sqlalchemy.dialects.mysql.MEDIUMTEXT

所以我尝试将 content_text = db.Column(MEDIUMTEXT(unicode=True)) 设置为 16 位,但在 cmets 的帮助下,我们得到了正确的 content_text =db.Column(MEDIUMTEXT(charset='utf8mb4')) 将在桌面上正确设置为 32 位。

只是为了确保我使用 md5 校验和验证了进入表格的 16 位字符内容是否相同。

【讨论】:

  • 您可以将列定义为 MEDIUMTEXT(charset='utf8mb4') 以覆盖整个 UTF-8 范围。
  • 设置charset='utf8mb4' 实际上创建了一个带有ucs2 的表我简要查看了mysql 方言以了解原因,但不清楚如何保持16 位。
  • @PeterMoore - charset="utf8mb4" works for me 使用 SQLAlchemy 1.4.23。您使用的是旧版本吗?
  • 啊我从 SQLAlchemy==1.4.20 和 Flask-Migrate==3.0.1 更新到 SQLAlchemy==1.4.23 和 Flask-Migrate==3.1.0 现在它确实创建了一个 utf8mb4 .好的,我会更新答案。感谢您的反馈。
  • @PeterMoore - utf8mb4 字符为 1、2、3 或 4 个字节。 ucs2 字符为 2 或 4 个字节;它们不一样。
猜你喜欢
  • 1970-01-01
  • 2020-11-18
  • 2017-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多