Flask Admin图片上传报错:先遇AttributeError后现ValueError求助
问题解决步骤
错误原因分析
- AttributeError: module 'PIL.Image' has no attribute 'ANTIALIAS':Pillow 10.0+版本将
ANTIALIAS重命名为Resampling.LANCZOS,而Flask-Admin旧版本的默认图片处理逻辑仍在使用ANTIALIAS。 - ValueError: not enough values to unpack (expected 3, got 2):你传入的
thumbnail_size格式不符合Flask-Admin预期——它支持两种格式:(宽度, 高度, 是否裁剪)的三元组,或者((宽度, 高度), {附加参数字典})的二元组,直接传((100,100), 采样器枚举)会导致解包失败。
解决方案
方案1:适配Pillow 10+版本(推荐)
修改代码,自定义图片上传字段以支持新的采样器枚举,同时设置正确的thumbnail_size:
- 调整导入语句,确保正确引入
Resampling:
from PIL import Image from PIL.Image import Resampling
- 自定义
ImageUploadField子类,替换默认的采样逻辑:
class CustomImageUploadField(form.ImageUploadField): def _resize_image(self, image, size): if isinstance(size, tuple): if len(size) == 2: # 仅调整尺寸,使用LANCZOS采样 image = image.resize(size, Resampling.LANCZOS) elif len(size) == 3: # 先裁剪再调整尺寸,使用LANCZOS采样 width, height, crop = size image = image.crop((0, 0, width, height)) image = image.resize((width, height), Resampling.LANCZOS) return image
- 在
ImageView的form_extra_fields中使用自定义字段,并设置正确的thumbnail_size:
form_extra_fields = { 'path': CustomImageUploadField('Image', base_path=file_path, # 需要裁剪时用这个格式 thumbnail_size=(100, 100, True) # 不需要裁剪时用这个格式 # thumbnail_size=(100, 100) ) }
方案2:降级Pillow到9.x版本
如果不想修改代码,可以运行以下命令降级Pillow,恢复旧版的ANTIALIAS支持:
pip install pillow==9.5.0
降级后,恢复原来的thumbnail_size=(100, 100, True)即可正常使用。
修改后的完整代码示例
import os import os.path as op from flask import Flask, url_for from flask_sqlalchemy import SQLAlchemy from markupsafe import Markup from flask_admin import Admin, form from flask_admin.contrib import sqla from PIL import Image from PIL.Image import Resampling app = Flask(__name__, static_folder='files') app.config['FLASK_ADMIN_SWATCH'] = 'cerulean' app.config['SECRET_KEY'] = '123456790' app.config['DATABASE_FILE'] = 'sample_db.sqlite' app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + app.config['DATABASE_FILE'] app.config['SQLALCHEMY_ECHO'] = True db = SQLAlchemy(app) # 创建文件存储目录 file_path = op.join(op.dirname(__file__), 'files') try: os.mkdir(file_path) except OSError: pass class Image(db.Model): id = db.Column(db.Integer, primary_key=True) name = db.Column(db.Unicode(64)) path = db.Column(db.Unicode(128)) def __unicode__(self): return self.name # 自定义图片上传字段,适配Pillow 10+ class CustomImageUploadField(form.ImageUploadField): def _resize_image(self, image, size): if isinstance(size, tuple): if len(size) == 2: image = image.resize(size, Resampling.LANCZOS) elif len(size) == 3: width, height, crop = size image = image.crop((0, 0, width, height)) image = image.resize((width, height), Resampling.LANCZOS) return image class ImageView(sqla.ModelView): def _list_thumbnail(view, context, model, name): if not model.path: return '' return Markup('<img src="%s">' % url_for('static', filename=form.thumbgen_filename(model.path))) column_formatters = { 'path': _list_thumbnail } form_extra_fields = { 'path': CustomImageUploadField('Image', base_path=file_path, thumbnail_size=(100, 100, True)) } @app.route('/') def index(): return '<a href="/admin/">Click me to get to Admin!</a>' admin = Admin(app, 'Example: Forms', template_mode='bootstrap4') admin.add_view(ImageView(Image, db.session)) def build_sample_db(): db.create_all() db.session.commit() return if __name__ == '__main__': app_dir = op.realpath(os.path.dirname(__file__)) database_path = op.join(app_dir, app.config['DATABASE_FILE']) if not os.path.exists(database_path): with app.app_context(): build_sample_db() app.run(debug=True)
内容的提问来源于stack exchange,提问作者hope7394 m
相关产品推荐
相关产品推荐

