mysql - Flask-Admin+SQLAlchemy 图像上传在 MySQL 生产环境中有效,但在 SQLite 测试用例 : InterfaceError 中无效

标签 mysql sqlite file-upload sqlalchemy flask-admin

我使用 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 .

最佳答案

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

正如您在堆栈跟踪中看到的,它正在尝试绑定(bind) 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')>,)]

你想放置断点的地方是方法 do_execute()sqlalchemy.engine.default模块。

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

另一方面,MySQL后端来自名为MySQLdb的纯Python模块。 .所以它调用MySQLdb.cursors.BaseCursor.execute()方法,特别是转换 FileStorage 类型的参数通过调用 db.literal() 到 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 的示例中有效?

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

如何修复

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

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

关于mysql - Flask-Admin+SQLAlchemy 图像上传在 MySQL 生产环境中有效,但在 SQLite 测试用例 : InterfaceError 中无效,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31287446/

相关文章:

MySQL LEFT JOIN 与 ON 子句中的一列

Mysql添加用户进行远程访问

php - 此查询中的错误是没有删除

Javascript - 文件上传安全检查是否存在恶意软件、病毒

node.js - 将本地文件夹中的图像上传到S3

PHP/MySQL 性能问题

java - 什么时候打开和关闭数据库是值得推荐的?

objective-c - 如何使用 SQLite 存储在 Core Data 中查询未保存的数据?

php - 统计和限制上传文件的数量(HTML文件输入)

mysql - 将为日期范围(从、到)指定的值应用于包含每个日期的单行的表中的值