如何使用SQLAlchemy保存unicode?

时间:2014-07-17 05:22:33

标签: python unicode sqlalchemy

我遇到过这样的错误:

File "/vagrant/env/local/lib/python2.7/site-packages/sqlalchemy/engine/default.py", line 435, in do_execute
            cursor.execute(statement, parameters)
        exceptions.UnicodeEncodeError: 'ascii' codec can't encode character u'\u2013' in position 8410: ordinal not in range(128)

当我试图用指定的Python unicode字符串保存ORM对象时,会发生这种情况。因此,dict parameters将unicode字符串作为其值之一,并在将其强制转换为str类型时产生错误。

我尝试在引擎和列上设置convert_unicode=True设置,但没有成功。

那么在SQLAlchemy中处理unicode的好方法是什么?

更新

这是关于我的设置的一些细节:

表格

                                    Table "public.documents"
   Column   |           Type           |                       Modifiers                        
------------+--------------------------+--------------------------------------------------------
 id         | integer                  | not null default nextval('documents_id_seq'::regclass)
 sha256     | text                     | not null
 url        | text                     | 
 source     | text                     | not null
 downloaded | timestamp with time zone | not null
 tags       | json                     | not null
Indexes:
    "documents_pkey" PRIMARY KEY, btree (id)
    "documents_sha256_key" UNIQUE CONSTRAINT, btree (sha256)

ORM模型:

class Document(Base):
    __tablename__ = 'documents'

    id = Column(INTEGER, primary_key=True)
    sha256 = Column(TEXT(convert_unicode=True), nullable=False, unique=True)
    url = Column(TEXT(convert_unicode=True))
    source = Column(TEXT(convert_unicode=True), nullable=False)
    downloaded = Column(DateTime(timezone=True), nullable=False)
    tags = Column(JSON, nullable=False)

SQLAlchemy设置:

ENGINE = create_engine('postgresql://me:secret@localhost/my_db',
                       encoding='utf8', convert_unicode=True)
Session = sessionmaker(bind=ENGINE)

产生错误的代码只是创建一个会话,实例化一个Document对象并使用分配给它的source字段with unicode` strign保存它。

更新#2

检查this repo - 它已自动设置Vagrant / Ansible,并重现此错误。

3 个答案:

答案 0 :(得分:10)

你的问题在这里:

$ sudo grep client_encoding /etc/postgresql/9.3/main/postgresql.conf
client_encoding            = sql_ascii

这导致psycopg2默认为ASCII:

>>> import psycopg2
>>> psycopg2.connect('dbname=dev_db user=dev').encoding
'SQLASCII'

...它有效地关闭了psycopg2处理Unicode的能力。

您可以在postgresql.conf中修复此问题:

client_encoding = utf8

(然后是sudo invoke-rc.d postgresql reload),或者您可以在创建引擎时明确指定编码:

self._conn = create_engine(src, client_encoding='utf8')
我推荐前者,因为九十年代初期早已不复存在。 :)

答案 1 :(得分:3)

我无法重现您的问题(您也没有包含有关如何将项目实际添加到数据库中的示例,可能存在错误)。但是,我建议您在与系统的其余部分完全隔离的情况下测试代码,以查看您想要执行的操作是否真正有效,而不会受到其他代码的干扰。我创建这个文件只是为了测试你想做什么,并且main方法将相关对象作为一行插入到数据库中。

# encoding: utf-8

from sqlalchemy import Column, Integer, String, Boolean, Float, Text
from sqlalchemy import Column, INTEGER, TEXT
from sqlalchemy import create_engine, MetaData
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

Base = declarative_base()


class Demo(Base):

    __tablename__ = 'demo'

    id = Column(INTEGER, primary_key=True)
    key = Column(TEXT(convert_unicode=True))
    value = Column(TEXT(convert_unicode=True))


class Backend(object):

    def __init__(self, src=None):
        if not src:
            src = 'sqlite://'

        self._conn = create_engine(src)
        self._metadata = MetaData()
        self._metadata.reflect(bind=self._conn)
        Base.metadata.create_all(self._conn)
        self._sessions = sessionmaker(bind=self._conn)

    def session(self):
        return self._sessions()


def main():
    backend = Backend('postgresql://postgres@localhost/test')
    s = backend.session()
    obj = Demo()
    obj.key = 'test'
    obj.value = u'–test–'
    s.add(obj)
    s.commit()
    return backend

在解释器中运行:

>>> b = main()
>>> s = b.session()
>>> s.query(Demo).get(1).value
u'\u2013test\u2013'

在psql中:

postgres=# \c test
You are now connected to database "test" as user "postgres".
test=# select * from demo;
 id | key  | value  
----+------+--------
  1 | test | –test–
(1 row)

很抱歉,我无法真正帮助您,但我希望这会指出您(或其他人)弄清楚您的代码为什么会出现unicode解码错误。我使用的软件版本是python-2.7.7,sqlalchemy-0.9.6,psycopg2-2.5.3,postgresql-9.3.4。

答案 2 :(得分:0)

我无法重现您的错误。我可以提供一些关于使用SQLAlchemy进行unicode处理的提示,这些技巧可能会有所帮助,也可能没有帮助:

  • 不使用convert_unicode,而只使用sqlalchemy.types.Unicode()列类型。这将永远是正确的。
  • 即使您使用'key',也要将str实例(key)分配给convert_unicode=True列。您要么指定unicode值,要么使用非unicode代码列类型。
  • 始终检查PostgreSQL数据库的编码是否正确设置为UTF-8。
  • 通常,您不需要create_engine的encodingconvert_unicode参数。