【问题标题】:Why BINARY usage in SQLAlchemy with Python3 cause a TypeError: 'string argument without an encoding'为什么在 Python3 的 SQLAlchemy 中使用 BINARY 会导致 TypeError: 'string argument without an encoding'
【发布时间】:2020-04-11 18:08:00
【问题描述】:

我阅读了很多类似的问题,但没有一个明确回答我的问题。

我在 mysql 表列上使用 sqlalchemy-utils EncryptedType
表创建和插入都可以,但是当我尝试进行查询时接收:

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 类型上,因为我在 BinaryVarbinaryBlob 列上遇到了同样的错误。
由于python3中的bytes需要对字符串进行编码,我将第944行的sqlalchemy\sql\sqltypes.py的代码更改为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()

【问题讨论】:

  • SQLA 在 Python 3 中已完全可用多年。我认为还有其他问题。您的数据库似乎返回的是文本而不是二进制数据。
  • 如果我使用mysql.connector方法connect(**DB_CONFIG)查询数据,我可以正确获取数据。所以也许我错过了一些 SQLA 配置。
  • 但是您的 DB-API 查询是否为相关列返回 bytesstrmysql.connector 仅使用 DB 提供的类型信息。
  • @IljaEverilä,你是对的,光标中“attr”列的类型是str。我还注意到,如果在插入语句中我输入了一些像'try'.encode('utf-8') 这样的数据,则使用mysql.connector 的查询可以正常工作,返回一个字符串,但是如果我输入像base64.b64decode('data'.encode('utf-8')) 这样的数据,则会出现以下错误UnicodeDecodeError: 'utf-8' codec can't decode byte 0xab in position 1: invalid start byte。跨度>
  • 很遗憾,我还是找不到解决办法

标签: python mysql python-3.x sqlalchemy sqlalchemy-utils


【解决方案1】:

MySQL 连接器似乎有什么问题。只需将您的 mysql-connector-python 切换为 mysqlclient。我遇到了同样的问题,它帮助了我。

【讨论】:

    【解决方案2】:

    您将拥有mysql+mysqldb://&lt;user&gt;:&lt;password&gt;@&lt;host&gt;[:&lt;port&gt;]/&lt;dbname&gt; 而不是mysql+mysqlconnector://&lt;user&gt;:&lt;password&gt;@&lt;host&gt;[:&lt;port&gt;]/&lt;dbname&gt;

    SQLAchemy 文档建议在 MySQL-Connector 上使用 mysqlclient(MySQL-Python 的分支)¶。

    MySQL 连接器/Python DBAPI 自发布以来存在许多问题,其中一些可能仍未解决,并且 mysqlconnector 方言未作为 SQLAlchemy 持续集成的一部分进行测试。推荐的 MySQL 方言是 mysqlclient 和 PyMySQL。

    【讨论】:

      【解决方案3】:

      感谢 Ilja Everilä 提供的提示,我能够找到解决方案。也许不是最好的解决方案,但现在正在奏效。

      我认为根本原因是我的 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”,您必须手动对其进行解码。

      肯定有更好的解决方案。

      【讨论】:

        猜你喜欢
        • 2020-05-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-02-06
        • 2021-03-06
        • 1970-01-01
        • 2021-01-19
        相关资源
        最近更新 更多