You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flask Admin图片上传报错:先遇AttributeError后现ValueError求助

问题解决步骤

错误原因分析

  1. AttributeError: module 'PIL.Image' has no attribute 'ANTIALIAS':Pillow 10.0+版本将ANTIALIAS重命名为Resampling.LANCZOS,而Flask-Admin旧版本的默认图片处理逻辑仍在使用ANTIALIAS。
  2. ValueError: not enough values to unpack (expected 3, got 2):你传入的thumbnail_size格式不符合Flask-Admin预期——它支持两种格式:(宽度, 高度, 是否裁剪)的三元组,或者((宽度, 高度), {附加参数字典})的二元组,直接传((100,100), 采样器枚举)会导致解包失败。

解决方案

方案1:适配Pillow 10+版本(推荐)

修改代码,自定义图片上传字段以支持新的采样器枚举,同时设置正确的thumbnail_size:

  1. 调整导入语句,确保正确引入Resampling:
from PIL import Image
from PIL.Image import Resampling
  1. 自定义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
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 13:44:53