我正在运行Django 1.4.2 / Python 2.7.3 / MySQL 5.5.28站点。该站点的一个功能是管理员可以向服务器发送一封电子邮件,该服务器通过procmail调用Python脚本,解析电子邮件并将其丢入数据库。我维护了两个版本的站点 - 开发和生产站点。两个站点使用不同但相同的vitualenvs(我甚至删除它们并重新安装所有包装以确保)。
我遇到了一个奇怪的问题。 完全相同的脚本在开发服务器上成功,在生产服务器上失败。它失败并出现此错误:
...django/db/backends/mysql/base.py:114: Warning: Incorrect string value: '\x92t kno...' for column 'message' at row 1
我很清楚Django的unicode问题,我知道这里有很多关于这个错误的问题,但我确保从一开始就将数据库设置为UTF-8:
mysql> show variables like "character_set_database";
+------------------------+-------+
| Variable_name | Value |
+------------------------+-------+
| character_set_database | utf8 |
+------------------------+-------+
1 row in set (0.00 sec)
mysql> show variables like "collation_database";
+--------------------+-----------------+
| Variable_name | Value |
+--------------------+-----------------+
| collation_database | utf8_general_ci |
+--------------------+-----------------+
1 row in set (0.00 sec)
此外,我知道每列都有自己的字符集,但message
列确实是UTF-8:
mysql> show full columns in listserv_post;
+------------+--------------+-----------------+------+-----+---------+----------------+---------------------------------+---------+
| Field | Type | Collation | Null | Key | Default | Extra | Privileges | Comment |
+------------+--------------+-----------------+------+-----+---------+----------------+---------------------------------+---------+
| id | int(11) | NULL | NO | PRI | NULL | auto_increment | select,insert,update,references | |
| thread_id | int(11) | NULL | NO | MUL | NULL | | select,insert,update,references | |
| timestamp | datetime | NULL | NO | | NULL | | select,insert,update,references | |
| from_name | varchar(100) | utf8_general_ci | NO | | NULL | | select,insert,update,references | |
| from_email | varchar(75) | utf8_general_ci | NO | | NULL | | select,insert,update,references | |
| message | longtext | utf8_general_ci | NO | | NULL | | select,insert,update,references | |
+------------+--------------+-----------------+------+-----+---------+----------------+---------------------------------+---------+
6 rows in set (0.00 sec)
有谁知道我为什么会收到这个错误?为什么它发生在生产配置下而不是dev配置?
谢谢!
[编辑1]
需要说明的是,数据也是一样的。我向服务器发送一封电子邮件,procmail将其发送出去。这就是.procmailrc的样子:
VERBOSE=off
:0
{
:0c
| <path>/dev/ein/scripts/process_new_mail.py dev > outputdev
:0
| <path>/prd/ein/scripts/process_new_mail.py prd > outputprd
}
有2份process_new_mail.py副本,但这只是因为它受版本控制,所以我可以维护两个独立的环境。如果我对两个输出文件(包含收到的消息)进行区分,则它们是相同的。
[编辑2]
我实际上刚刚发现dev和prd配置都失败了。不同之处在于dev配置无提示失败(可能与DEBUG
设置有关?)。问题是其中一条消息中有一些unicode字符,而Django由于某种原因而窒息它们。我正在取得进步......
我已经尝试编辑代码以将消息显式编码为ASCII和UTF-8,但它仍然无效。不过,我越来越近了。
答案 0 :(得分:1)
我修好了!问题是我没有正确解析关于charsets的电子邮件。我的固定电子邮件解析代码来自this post和this post:
#get the charset of an email
#courtesy http://ginstrom.com/scribbles/2007/11/19/parsing-multilingual-email-with-python/
def get_charset(message, default='ascii'):
if message.get_content_charset():
return message.get_content_charset()
if message.get_charset():
return message.get_charset()
return default
#courtesy https://stackoverflow.com/questions/7166922/extracting-the-body-of-an-email-from-mbox-file-decoding-it-to-plain-text-regard
def get_body(message):
body = None
#Walk through the parts of the email to find the text body.
if message.is_multipart():
for part in message.walk():
#If part is multipart, walk through the subparts.
if part.is_multipart():
for subpart in part.walk():
if subpart.get_content_type() == 'text/plain':
#Get the subpart payload (i.e., the message body).
charset = get_charset(subpart, get_charset(message))
body = unicode(subpart.get_payload(decode=True), charset)
#Part isn't multipart so get the email body.
elif part.get_content_type() == 'text/plain':
charset = get_charset(subpart, get_charset(message))
body = unicode(part.get_payload(decode=True), charset)
#If this isn't a multi-part message then get the payload (i.e., the message body).
elif message.get_content_type() == 'text/plain':
charset = get_charset(subpart, get_charset(message))
body = unicode(message.get_payload(decode=True), charset)
return body
非常感谢您的帮助!