Django结合Pillow生成贺卡图片无法存库及返回用户的问题
核心错误点
- Pillow的
Image.save()方法无返回值,直接将其返回值(None)赋值给ImageField字段,不可能存入有效图片 - 视图未区分GET/POST请求,用户首次打开页面时就会尝试读取POST参数触发异常
- 图片存储逻辑未走Django文件存储机制,直接将图片写入项目根目录,无法被Django识别访问
- 模板未接收生成的贺卡实例,且
ImageField字段渲染方式错误,无法正常展示图片 - 代码中硬编码Windows绝对路径,兼容性极差,换运行环境就会报错
修复步骤
1. 补全媒体文件配置
首先在项目settings.py末尾添加媒体文件配置:
import os MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
再打开项目根路由文件urls.py,添加开发环境下的媒体文件访问路由:
from django.conf import settings from django.conf.urls.static import static urlpatterns = [ # 保留你原有的路由配置 ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
2. 重写views.py逻辑
用内存流处理Pillow生成的图片,走Django官方文件存储API保存,同时区分请求方法:
from django.shortcuts import render from .models import Name from PIL import Image, ImageDraw, ImageFont from django.core.files.base import ContentFile from io import BytesIO import os from django.conf import settings def Home(request): # GET请求直接返回空表单页面 if request.method == "GET": return render(request, 'index.html') # POST请求处理贺卡生成 name_input = request.POST.get('user_name', '').strip() if not name_input: return render(request, 'index.html', {'error': '请输入姓名'}) # 用BASE_DIR拼接文件路径,替换硬编码的绝对路径 base_img_path = os.path.join(settings.BASE_DIR, 'files', 'covers', 'mypic.png') font_path = os.path.join(settings.BASE_DIR, 'fonts', 'static', 'Cairo-Bold.ttf') # Pillow绘制贺卡 img = Image.open(base_img_path) draw = ImageDraw.Draw(img) font = ImageFont.truetype(font_path, 40) draw.text((540,1020), name_input, font=font, fill=(237, 185, 117), anchor="ms") # 将生成的图片写入内存二进制流,无需提前落盘到本地 img_buffer = BytesIO() img.save(img_buffer, format='PNG') img_buffer.seek(0) # 将流指针移回起始位置 # 存入数据库 card_instance = Name(massenger_name=name_input) img_filename = f"eid_card_{name_input}_{card_instance.action_time.strftime('%Y%m%d%H%M%S')}.png" card_instance.image.save(img_filename, ContentFile(img_buffer.read()), save=False) card_instance.save() # 把生成的贺卡实例传给模板 return render(request, 'index.html', {'card': card_instance})
3. 修复模板渲染逻辑
修正表单属性和图片渲染代码,替换原index.html中对应部分:
<!DOCTYPE html> <html lang="en" dir="rtl"> <head> <meta charset="utf-8"> <title>节日贺卡生成</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous"> {% load static %} <link rel="stylesheet" href="{% static 'CSS/style.css' %}"> </head> <body> <div id="form"> <form class="row" method="POST" enctype="multipart/form-data"> {% csrf_token %} <div class=""> <input type="text" class="form-control" placeholder="أكتب أسمك (مثلا/أخوكم عبدالله العتيبي)" id="text_name" name="user_name" required> </div> <div class="col-auto"> <button type="submit" class="btn btn-primary mb-3" id="button">生成贺卡</button> </div> </form> <!-- 生成成功后展示贺卡 --> {% if card %} <div class="result-area mt-4"> <p>你的贺卡已生成:</p> <img src="{{ card.image.url }}" alt="{{ card.massenger_name }}的节日贺卡" class="img-fluid"> <a href="{{ card.image.url }}" download class="btn btn-success mt-2">下载贺卡</a> </div> {% endif %} {% if error %} <p class="text-danger mt-2">{{ error }}</p> {% endif %} </div> </body> </html>
额外注意事项
- 确认项目根目录下
media文件夹存在,且Django进程对该文件夹有读写权限 - 原代码中输入框的
type="textarea"是错误写法,单行文本输入使用type="text"即可 - 图片文件名加了时间戳后缀,避免多用户同名提交导致文件覆盖
内容的提问来源于stack exchange,提问作者data data
相关产品推荐
相关产品推荐

