【问题标题】:Flask-Admin+SQLAlchemy image upload works in MySQL production env but not in SQLite testcase: InterfaceErrorFlask-Admin+SQLAlchemy 图像上传适用于 MySQL 生产环境,但不适用于 SQLite 测试用例:InterfaceError
【发布时间】:2015-09-26 01:19:42
【问题描述】:

我使用 Flask、Flask-Admin 和 Flask-SQLAlchemy 创建了一个 Web 应用程序,管理员可以在其中上传图像。图片上传功能主要模仿this Flask-Admin example。对于生产网站,我使用 MySQL 并且上传工作非常好。但是,在测试套件中,我使用了内存映射 SQLite 数据库,并且任何通过相同表单上传图像的尝试都会失败,并返回 InterfaceError。请参阅this Gist 了解完整的详细信息和简化的测试用例。

似乎它可能与类型映射有关,其中 SQLAlchemy 的 MySQL 后端似乎理解上传图像的文件名必须插入到 SQL 语句中,而 SQLite 后端没有。但是,我在上面链接到的 Flask-Admin 示例工作得非常好,它也是基于 SQLite。

谁能告诉我哪里出了问题,需要做什么才能使测试通过?

编辑添加:事实证明这个问题已经被 Flask-Admin 开发人员知道了。见ticket on GitHub

【问题讨论】:

  • 您可以打印包含 SelectField() 和 QuerySelectField() 的类吗?
  • @kuttyraj 我不明白你的要求,你能详细说明一下吗?

标签: mysql sqlite file-upload sqlalchemy flask-admin


【解决方案1】:

是的,你是对的。这就是你的问题的原因。问题在于 SQLite 和 MySQL 后端的不同。

正如您在堆栈跟踪中看到的那样,它正在尝试绑定 FileStorage 类型的参数,但失败了。

InterfaceError: (sqlite3.InterfaceError) Error binding parameter 0 - probably unsupported type. [SQL: u'SELECT picture.id AS picture_id, picture.name AS picture_name, picture.path AS picture_path \nFROM picture \nWHERE picture.path = ?'] [parameters: (<FileStorage: u'openclipart_hector_gomez_landscape.png' ('image/png')>,)]

您要放置断点的位置将在 sqlalchemy.engine.default 模块中的方法 do_execute() 处。

SQLite 后端是二进制扩展,cursor 来自二进制扩展 (_sqlite3.so)。此二进制扩展获取FileStorage 类型的参数,并尝试通过调用FileStorage.__conform__() 方法将其转换为SQL 表示。但是类没有这样的方法。这就是它失败的原因。

另一方面,MySQL 后端来自名为 MySQLdb 的纯 Python 模块。因此它调用MySQLdb.cursors.BaseCursor.execute() 方法,特别是通过调用db.literal()FileStorage 类型的参数转换为SQL 表示形式,最终调用FileStorage.__repr__()。最后得到以下查询:

'SELECT picture.id AS picture_id, picture.name AS picture_name, picture.path AS picture_path FROM picture WHERE picture.path = \\'<FileStorage: u\\\\'images.jpeg\\\\' (\\\\'image/jpeg\\\\')>\\''

没想到吧?现在您不太确定它是否可以正确与 MySQL 一起使用。你是?只需尝试使用相同的文件创建两张图片,您将收到 Integrity error. (_mysql_exceptions.IntegrityError) (1062, "Duplicate entry 'images.jpeg' for key 'path'") [SQL: u'INSERT INTO picture (name, path) VALUES (%s, %s)'] [parameters: ('Test', 'images.jpeg')] 而不是有意义的错误消息。

为什么它在 Flask-Admin 的示例中有效?

您在模型的path 列上设置了unique 约束。这正是失败的 SQL 查询的来源。在尝试插入新图片之前,它会检查数据库中是否已经存在具有相同路径的图片。

如何解决

问题是flask_admin.contrib.sqla.validators.Unique 验证器或flask_admin.form.upload.FileUploadField 中的错误(它们不兼容)。验证器应该使用与flask_admin.form.upload.FileUploadField.populate_obj() 方法放入模型中的相同值,而不是直接将FileStorage 传递给数据库查询。只需在 GitHub 上 raise an issue 并参考这个问题。

我不认为它可以在您的测试用例中轻松修复,因为它是您所依赖的库中的一个非常重要的错误。当然,前提是您希望在您的 path 字段上保留 unique 约束。

【讨论】:

  • 优秀的答案,很好的研究。我会按照你的建议提交一张票。
猜你喜欢
  • 2022-11-24
  • 2013-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-24
  • 1970-01-01
  • 2011-06-12
  • 2013-11-08
相关资源
最近更新 更多