Как сохранить 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)

это происходит, когда я пытаюсь сохранить объект ORM с назначенным Python unicode строку. И в результате dict parameters имеет строку unicode в качестве одного из своих значений, и она создает ошибку, принуждая ее к str тип.

я пытался установить convert_unicode=True установка на двигатель и колонку, но без успеха.

Итак, каков хороший способ обработки unicode в С SQLAlchemy?

обновление

это некоторые подробности о моей установке:

стол:

                                    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 settngs:

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

и код, который создает ошибку, просто создает сеанс, создает экземпляр Document объект и сохраняет его с sourceполеwithunicode ' strign назначен ему.

обновление #2

Регистрация этой repo-он имеет автоматическую настройку Vagrant/Ansible и воспроизводит эту ошибку.

3 ответов


ваша проблема здесь:

$ 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')

я рекомендую первое, потому что начало девяностых давно прошло. : )


Я не могу воспроизвести вашу проблему (также вы не включили примеры того, как вы фактически добавляете свои элементы в базу данных, ошибка может быть там). Однако я рекомендую вам протестировать свой код в полной изоляции с остальной частью вашей системы, чтобы увидеть, действительно ли то, что вы хотите сделать, работает без вмешательства вашего другого кода. Я создал этот файл исключительно для проверки того, работает ли то, что вы хотите сделать, и метод 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.


Я не могу воспроизвести вашу ошибку. Я могу предоставить несколько советов по обработке unicode с помощью SQLAlchemy, которые могут помочь или не помочь:

  • вместо convert_unicode просто используйте sqlalchemy.типы.Юникод() тип столбца. Это всегда будет правильно.
  • вы назначаете экземпляр str ('key') к