如何配置sqlalchemy以正确存储表情符号?

Cra*_*ght 6 python mysql sqlalchemy

使用sqlalchemy 0.9.7,我试图在启用utf8mb4的情况下将表情符号存储到MySQL 5.5中.然而,由于某种原因,sqlalchemy正在杀死我的表情符号字符,我无法弄清楚为什么.在尝试通过sqlalchemy保存数据之前,我可以在对象中看到表情符号字符.保存后,抛出错误,表情符号呈现为????.

错误如下.请注意,它被输出type(post.message)和调试消息所包围post.message.

--------------------------------------------------------------------------------
DEBUG in __init__ [/mnt/hgfs/crw/dev/hyper/hyper/blueprint/chat/__init__.py:274]:
<type 'unicode'>
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
DEBUG in __init__ [/mnt/hgfs/crw/dev/hyper/hyper/blueprint/chat/__init__.py:275]:

--------------------------------------------------------------------------------
/mnt/hgfs/crw/dev/hyper/env/local/lib/python2.7/site-packages/sqlalchemy/engine/default.py:436: Warning: Incorrect string value: '\xF0\x9F\x98\x83' for column 'message' at row 1
  cursor.execute(statement, parameters)
--------------------------------------------------------------------------------
DEBUG in __init__ [/mnt/hgfs/crw/dev/hyper/hyper/blueprint/chat/__init__.py:277]:
<type 'unicode'>
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
DEBUG in __init__ [/mnt/hgfs/crw/dev/hyper/hyper/blueprint/chat/__init__.py:278]:
????
--------------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)

我的代码看起来像:

Base = declarative_base()

post = Table('post', Base.metadata,
        Column("id", Integer, primary_key=True),
        Column("message", UnicodeText),
        Column("created_at", DateTime),
        Column("updated_at", DateTime),
    )

mapper(Post, post)
Run Code Online (Sandbox Code Playgroud)

我需要做任何其他设置才能使其工作吗?

更新:如果我执行以下操作:

Base = declarative_base()

post = Table('post', Base.metadata,
        Column("id", Integer, primary_key=True),
        Column("message", UnicodeText),
        Column("created_at", DateTime),
        Column("updated_at", DateTime),
        mysql_engine='InnoDB',
        mysql_charset='utf8mb4'
    )

mapper(Post, post)
Run Code Online (Sandbox Code Playgroud)

我得到以下内容:

--------------------------------------------------------------------------------
DEBUG in __init__ [/mnt/hgfs/crw/dev/hyper/hyper/blueprint/chat/__init__.py:274]:
<type 'unicode'>
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
DEBUG in __init__ [/mnt/hgfs/crw/dev/hyper/hyper/blueprint/chat/__init__.py:275]:

--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
DEBUG in __init__ [/mnt/hgfs/crw/dev/hyper/hyper/blueprint/chat/__init__.py:277]:
<type 'unicode'>
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
DEBUG in __init__ [/mnt/hgfs/crw/dev/hyper/hyper/blueprint/chat/__init__.py:278]:
????
--------------------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)

没有错误,但它仍然消灭了表情符号.到底是怎么回事?!

更新2:

更改要调用的数据库URI:

mysql+mysqldb://user:pass@localhost/datab?charset=utf8
Run Code Online (Sandbox Code Playgroud)

mysql+mysqldb://user:pass@localhost/datab
Run Code Online (Sandbox Code Playgroud)

解决了这个问题.sqlalchemy在我尝试使用时抛出了一个错误charset=utf8mb4&use_unicode=0.但那是明智的吗?根据http://docs.sqlalchemy.org/en/rel_0_9/dialects/mysql.html#module-sqlalchemy.dialects.mysql.mysqldb,它不是!非常喜欢这个解决方案的任何反馈.

Nat*_*les 9

下面是我如何让表情符号等工作。我正在使用 Python 3.5/Flask/Flask-SQLAlchemy。

注意:此修复假设您在开发中的时间足够早,可以通过运行db.drop_all()和重新创建数据库db.create_all()

  1. 按照本指南中的建议,打开一个数据库控制台并运行ALTER DATABASE database_name CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;

  2. 按照此答案中的建议,添加?charset=utf8mb4SQLALCHEMY_DATABASE_URI字符串的末尾。

    • 前: mysql+mysqlconnector://{username}:{password}@{hostname}/{databasename}
    • 后: mysql+mysqlconnector://{username}:{password}@{hostname}/{databasename}?charset=utf8mb4
  3. 现在只需重新运行db.drop_all()db.create_all()


Dev*_*evy 5

这是对我有用的解决方案。set_unicode连接第一次进入连接池时被调用,并且只被调用一次。SET NAMES基本上修复了从代码到数据库存储的整个管道,以确保使用正确的 UTF-8 字符集 ( utf8mb4)。只要utf8mb4确保端到端,表情符号就应该被很好地存储/呈现,因为它们只是 BMP(基本多语言平面)之外的常规 unicode 字符,不幸的是 MySQL 决定只实现 3 字节 Unicode 实现是一个好主意为 BMP 作为unicode字符集。

请参阅SQLAlchemy 事件参考以了解其他相关事件挂钩,您可以利用这些挂钩通过连接配置调整进行连接。

import logging
from sqlalchemy import event

logger = logging.getLogger(__name__)     

@event.listens_for(Pool, "connect")
def set_unicode(dbapi_conn, conn_record):
    cursor = dbapi_conn.cursor()
    try:
        cursor.execute("SET NAMES 'utf8mb4' COLLATE 'utf8mb4_unicode_ci'")
    except Exception as e:
        logger.debug(e)
Run Code Online (Sandbox Code Playgroud)

更新:关于连接字符串中的额外选项,我更喜欢明确并指定,charset=utf8mb4因为我的环境的默认字符集是utf8(这是为什么表情符号无法正确编码的罪魁祸首),但use_unicode=0除非您在 python 2.x 和性能上运行,否则永远不要指定正如您在链接中指出的那样,这是一个瓶颈。