Почему использование BINARY в SQLAlchemy с Python3 вызывает TypeError: «строковый аргумент без кодировки» - PullRequest
1 голос
/ 11 апреля 2020

Я прочитал много похожих вопросов, но ни один из них четко не ответил на мой вопрос.

Я использую sqlalchemy-utils EncryptedType в столбце таблицы mysql.
Создание таблицы и вставка в порядке, но когда я пытаюсь выполнить запрос, получаю:

Traceback (most recent call last):
  File "workspace/bin/test.py", line 127, in <module>
    result = session.query(Tester).all()
  File "workspace\ERP\venv\lib\site-packages\sqlalchemy\orm\query.py", line 3244, in all
    return list(self)
  File "workspace\venv\lib\site-packages\sqlalchemy\orm\loading.py", line 101, in instances
    cursor.close()
  File "workspace\venv\lib\site-packages\sqlalchemy\util\langhelpers.py", line 69, in __exit__
    exc_value, with_traceback=exc_tb,
  File "workspace\venv\lib\site-packages\sqlalchemy\util\compat.py", line 178, in raise_
    raise exception
  File "workspace\venv\lib\site-packages\sqlalchemy\orm\loading.py", line 81, in instances
    rows = [proc(row) for row in fetch]
  File "workspace\venv\lib\site-packages\sqlalchemy\orm\loading.py", line 81, in <listcomp>
    rows = [proc(row) for row in fetch]
  File "workspace\venv\lib\site-packages\sqlalchemy\orm\loading.py", line 642, in _instance
    populators,
  File "workspace\venv\lib\site-packages\sqlalchemy\orm\loading.py", line 779, in _populate_partial
    dict_[key] = getter(row)
  File "workspace\venv\lib\site-packages\sqlalchemy\engine\result.py", line 107, in __getitem__
    return processor(self._row[index])
  File "workspace\venv\lib\site-packages\sqlalchemy\sql\sqltypes.py", line 944, in process
    value = bytes(value)
TypeError: string argument without an encoding

Я обнаружил, что эта ошибка возникает только при использовании python 3, не используя python 2.
А также, что проблема связана с типом sqlalchemy bynary, потому что я получаю ту же ошибку со столбцами Binary, Varbinary и Blob.
Поскольку bytes в python3 требуется кодировка для строк я изменил код sqlalchemy\sql\sqltypes.py в строке 944 на value = bytes(value, 'utf-8), и все работает хорошо, поэтому мой вопрос:

почему мне нужно изменить код sqlalchemy? Можно ли использовать sqlalchemy с python3? Безопасно ли менять код пакета?

Вот пример кода, который можно попробовать:

from sqlalchemy import MetaData, Integer, Column, Table, Binary, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, scoped_session

DB_CONFIG = {
        'user': 'user_test',
        'password': 'PSW_test',
        'host': '127.0.0.1',
        'database': 'db_test',
        }

if __name__ == '__main__':
    Base = declarative_base()
    engine = create_engine("mysql+mysqlconnector://%(user)s:%(password)s@%(host)s/%(database)s" % DB_CONFIG,
                           echo=False)
    Base.metadata.bind = engine
    db_sessionmaker = sessionmaker(bind=engine)
    Session = scoped_session(db_sessionmaker)

    # create the table
    meta = MetaData()
    tests = Table(
        'test', meta,
        Column('id', Integer, primary_key=True),
        Column('attr', Binary)
    )
    meta.create_all(engine)


    class Test(Base):
        __tablename__ = 'test'
        id = Column(Integer, primary_key=True)
        attr = Column(Binary)

    new_test = Test(attr='try'.encode('utf-8'))
    session = Session()
    session.add(new_test)
    session.commit()
    result = session.query(Test).all()
    for a in result:
        print(a, a.id, a.attr)
    Session.remove()

Ответы [ 2 ]

1 голос
/ 21 апреля 2020

Благодаря подсказке Ильи Эвериля я смог найти решение. Возможно, не лучшее решение, но сейчас оно работает.

Я думаю, что причина root в том, что мой DB-API автоматически преобразует bytes в str во время запроса. Поэтому я просто отключил это поведение, добавив параметр в create_engine:

engine = create_engine("mysql+mysqlconnector://%(user)s:%(password)s@%(host)s/%(database)s" % DB_CONFIG, connect_args={'use_unicode': False})

В результате, если у вас есть столбец String, он будет возвращен в запросах как bytes не как 'str', и вы должны вручную декодировать его.

Конечно, есть лучшее решение.

0 голосов
/ 06 мая 2020

Кажется, что-то не так с разъемом MySQL. Просто переключите ваш mysql-connector-python на mysqlclient. У меня была такая же проблема, и она мне помогла.

...