使用Paramiko搭建SFTP时查询ORM对象遇TypeError编码错误
Paramiko SFTP认证报错
TypeError: string argument without an encoding,SQLAlchemy LargeBinary字段实际为character varying 问题描述
用Python的Paramiko搭建SFTP服务器,通过SQLAlchemy连接PostgreSQL存储用户凭证。使用FileZilla/WinSCP密码登录时,authenticate_user函数触发TypeError: string argument without an encoding,错误栈指向SQLAlchemy查询结果执行bytes(value)的步骤。
在User类中将password_hash字段定义为LargeBinary类型,但通过SQLAlchemy inspect工具和pgAdmin4检查发现,数据库中该列实际类型为character varying。想明确:
- 代码定义与实际列类型不符的原因
- 这是否是触发认证错误的根源
相关代码片段
User类定义
from sqlalchemy import Column, Integer, String, LargeBinary from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class User(Base): __tablename__ = 'users' id = Column(Integer, primary_key=True) username = Column(String(50), unique=True, nullable=False) password_hash = Column(LargeBinary, nullable=False)
认证函数
def authenticate_user(username, password): from sqlalchemy.orm import sessionmaker from your_db_module import engine, User Session = sessionmaker(bind=engine) session = Session() user = session.query(User).filter_by(username=username).first() session.close() if user: import bcrypt # 错误触发点 return bcrypt.checkpw(password.encode('utf-8'), bytes(user.password_hash)) return False
错误栈
TypeError: string argument without an encoding Traceback (most recent call last): File "/path/to/your/sftp_server.py", line 45, in authenticate_user return bcrypt.checkpw(password.encode('utf-8'), bytes(user.password_hash)) TypeError: string argument without an encoding
问题分析与解决
1. LargeBinary变成character varying的原因
- 表结构未同步:如果在定义User类并执行
Base.metadata.create_all(engine)之前,数据库中已存在users表,且password_hash列是character varying类型,SQLAlchemy默认不会修改已存在的表结构(需用Alembic等迁移工具主动同步)。 - 模型修改后未更新表:修改User类的字段类型后,未重新生成表或执行迁移,导致数据库结构与代码定义脱节。
- 历史遗留问题:早期版本的模型定义中该字段是
String类型,后续修改为LargeBinary但未更新数据库。
2. 字段类型不符是认证错误的根源
是的,原因如下:
- 数据库中
password_hash存储为字符串(character varying),SQLAlchemy查询后返回Python字符串类型。 - 调用
bytes(user.password_hash)时,字符串转bytes必须指定编码(如bytes(str_val, 'utf-8')),未传编码参数会直接触发TypeError。 - 若字段为正确的
LargeBinary类型,SQLAlchemy会直接返回bytes对象,无需额外转换,也就不会报错。
3. 解决步骤
方案一:修正数据库表结构(推荐)
- 手动在pgAdmin4中将
users.password_hash列类型修改为bytea(PostgreSQL对应SQLAlchemy LargeBinary的原生类型)。 - 生产环境建议用Alembic生成迁移脚本,自动同步表结构,避免数据丢失。
方案二:同步模型与数据库(开发环境)
删除旧表后重新执行表生成命令:
Base.metadata.drop_all(engine) Base.metadata.create_all(engine)
注意:该操作会清空表数据,仅适用于开发测试环境
方案三:临时兼容(权宜之计)
若暂时无法修改表结构,可临时指定编码转换:
return bcrypt.checkpw(password.encode('utf-8'), bytes(user.password_hash, 'utf-8'))
但这只是临时方案,长期必须保证模型与数据库字段类型一致。
内容的提问来源于stack exchange,提问作者MagoLione
相关产品推荐
相关产品推荐

