Django在MySQL utf8字符集下存储表情符号的机制及读取异常排查
关于Django 1.4存储表情符号到MySQL utf8字段的问题解析
嘿,这个问题我太熟悉了,我来给你拆解一下当时Django的处理逻辑:
核心前提:MySQL的utf8是“阉割版”UTF-8
首先得明确一个关键细节:MySQL里的utf8字符集并不是完整的UTF-8——它只支持最多3字节的UTF-8字符,而表情符号(比如😊这类)属于4字节的UTF-8字符,原本就不在MySQL utf8的支持范围内。
Django 1.4的存储逻辑
在Django 1.4时代(大多搭配Python 2.x),当你把包含4字节表情符号的字符串存入MySQL utf8字段时,Django其实是直接把原始的4字节UTF-8字节序列写入了数据库:
- Django当时并没有对这些超出MySQL
utf8范围的字符做特殊转码或过滤(除非你自己手动加了校验逻辑) - 当时MySQL默认的
sql_mode大概率没开启严格校验,所以遇到无法识别的4字节序列时,并没有报错,而是直接把这些字节原封不动存在了字段里——这就导致数据库里的这些字符串实际上是不符合MySQLutf8规范的无效UTF-8字节序列
为什么当时Django运行正常?
这和Python 2的字符串机制直接相关:
- Python 2的
str类型是字节串,它不会自动校验UTF-8的有效性,只要你不对这些字节做严格解码(比如转成unicode类型时用严格模式),就不会触发异常 - 当Django把这些字节取出来渲染到前端时,浏览器本身支持完整的UTF-8(包括4字节字符),所以能正常显示表情符号,看起来一切都没问题
为什么Ruby读取会报错?
Ruby的字符串默认严格遵循UTF-8规范,当它从数据库读取到这些4字节序列时,会判定这是无效的UTF-8字节序列,所以直接抛出invalid byte sequence in utf-8异常——Ruby不像Python 2那样允许“脏”字节串存在,它对UTF-8的校验要严格得多。
内容的提问来源于stack exchange,提问作者majkel
相关产品推荐
相关产品推荐

