如何在Wagtail后台实现多图片上传功能(基于Django API架构)
Wagtail后台多图上传功能实现方案
我基于Django、Wagtail及API序列化器进行开发,不直接使用模板渲染HTML,而是通过Django和Wagtail的API功能输出内容。当前需要在后台新增多图片上传功能:默认Wagtail仅支持单次上传单张图片,我需在现有结构中实现wagtail/images/multiple/add路径下的多图上传能力。
现有代码结构如下:
blocks.py
from wagtail.images.blocks import ImageChooserBlock from wagtail.models import Site, Page from cms.core.models import get_rootless_url_path import re from wagtail.images.models import Image from wagtail.templatetags.wagtailcore_tags import richtext class ImageChooserRenditionsAPIBlock(ImageChooserBlock): def get_api_representation(self, value, context=None) -> dict: from .serializers import ImageSerializer return ImageSerializer(value).data class GreeterBlock(StructBlock): label = TextBlock() # description = TextBlock() description =CustomRichTextBlock(features=['h1', 'h2', 'h3', 'h4', 'h5', 'bold', 'italic', 'ol', 'ul', 'hr', 'link', 'image', 'code', 'blockquote'] ) image = ImageChooserRenditionsAPIBlock(required=False) video = CustomDocumentChooserBlock(required=False) cards = StreamBlock( local_blocks=("cards", CardBlock()), required=False, )
serializer.py
class ImageSerializer(serializers.ModelSerializer): class Meta: model = get_image_model() # fields = ("id", "title", "width", "height", "renditions") fields = ("id", "title", "width", "height", "file")
urls.py
from django.urls import path, include from wagtail import urls as wagtail_urls from wagtail.admin import urls as wagtailadmin_urls from wagtail.documents import urls as wagtaildocs_urls from wagtail.api.v2.router import WagtailAPIRouter from wagtail.documents.api.v2.views import DocumentsAPIViewSet from wagtail.images.api.v2.views import ImagesAPIViewSet from cms.meta import urls as meta_urls from .views import CustomPagesAPIViewSet api_router = WagtailAPIRouter("wagtailapi") api_router.register_endpoint("pages", CustomPagesAPIViewSet) api_router.register_endpoint("images", ImagesAPIViewSet) api_router.register_endpoint("documents", DocumentsAPIViewSet) urlpatterns = [ path("", include(wagtailadmin_urls)), path("content/documents/", include(wagtaildocs_urls)), ] api_urlpatterns = [ path("", api_router.urls), path("meta/", include(meta_urls.urlpatterns)), ]
实现步骤
1. 编写多图上传视图
在项目的views.py中新增批量图片上传处理逻辑:
from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt from wagtail.images.models import Image from wagtail.images.forms import get_image_form from wagtail.admin.auth import require_admin_access @require_admin_access @csrf_exempt def multiple_image_upload(request): if request.method == 'POST': ImageForm = get_image_form() uploaded_images = [] # 遍历上传的所有文件 for file in request.FILES.getlist('files'): form = ImageForm(request.POST, request.FILES, instance=Image()) if form.is_valid(): image = form.save() # 用现有序列化器返回图片数据 from .serializers import ImageSerializer uploaded_images.append(ImageSerializer(image).data) else: return JsonResponse({'error': form.errors}, status=400) return JsonResponse({'images': uploaded_images}, status=201) return JsonResponse({'error': '仅支持POST请求'}, status=405)
2. 配置多图上传路由
修改urls.py,在urlpatterns中添加对应路由:
from django.urls import path, include from wagtail import urls as wagtail_urls from wagtail.admin import urls as wagtailadmin_urls from wagtail.documents import urls as wagtaildocs_urls from wagtail.api.v2.router import WagtailAPIRouter from wagtail.documents.api.v2.views import DocumentsAPIViewSet from wagtail.images.api.v2.views import ImagesAPIViewSet from cms.meta import urls as meta_urls from .views import CustomPagesAPIViewSet, multiple_image_upload # 导入新增视图 api_router = WagtailAPIRouter("wagtailapi") api_router.register_endpoint("pages", CustomPagesAPIViewSet) api_router.register_endpoint("images", ImagesAPIViewSet) api_router.register_endpoint("documents", DocumentsAPIViewSet) urlpatterns = [ path("", include(wagtailadmin_urls)), path("content/documents/", include(wagtaildocs_urls)), # 新增多图上传路由 path("wagtail/images/multiple/add/", multiple_image_upload, name='multiple_image_upload'), ] api_urlpatterns = [ path("", api_router.urls), path("meta/", include(meta_urls.urlpatterns)), ]
3. (可选)在区块中支持多图片选择
如果需要在页面区块内直接选择多张图片,修改blocks.py新增多图选择区块:
from wagtail.images.blocks import ImageChooserBlock from wagtail.models import Site, Page from cms.core.models import get_rootless_url_path import re from wagtail.images.models import Image from wagtail.templatetags.wagtailcore_tags import richtext from wagtail.core.blocks import ListBlock, StructBlock, TextBlock, StreamBlock # 补充导入 class ImageChooserRenditionsAPIBlock(ImageChooserBlock): def get_api_representation(self, value, context=None) -> dict: from .serializers import ImageSerializer return ImageSerializer(value).data # 新增多图片选择区块 class MultiImageChooserAPIBlock(ListBlock): def __init__(self, **kwargs): super().__init__(ImageChooserRenditionsAPIBlock(), **kwargs) def get_api_representation(self, value, context=None): return [ self.child_blocks['image_chooser_renditions_api_block'].get_api_representation(item, context) for item in value ] class GreeterBlock(StructBlock): label = TextBlock() description = CustomRichTextBlock(features=['h1', 'h2', 'h3', 'h4', 'h5', 'bold', 'italic', 'ol', 'ul', 'hr', 'link', 'image', 'code', 'blockquote']) image = ImageChooserRenditionsAPIBlock(required=False) multi_images = MultiImageChooserAPIBlock(required=False) # 添加多图片字段 video = CustomDocumentChooserBlock(required=False) cards = StreamBlock( local_blocks=("cards", CardBlock()), required=False, )
4. (可选)优化序列化器返回缩略图
如果需要API返回不同尺寸的图片缩略图,修改serializer.py:
from rest_framework import serializers from wagtail.images.models import get_image_model, Rendition from wagtail.images.shortcuts import get_rendition_or_not_found class RenditionSerializer(serializers.ModelSerializer): class Meta: model = Rendition fields = ('url', 'width', 'height') class ImageSerializer(serializers.ModelSerializer): renditions = serializers.SerializerMethodField() class Meta: model = get_image_model() fields = ("id", "title", "width", "height", "file", "renditions") def get_renditions(self, obj): # 自定义需要返回的缩略图规格 rendition_specs = ['fill-300x200', 'max-800x600', 'original'] renditions = {} for spec in rendition_specs: rendition = get_rendition_or_not_found(obj, spec) renditions[spec] = RenditionSerializer(rendition).data return renditions
内容的提问来源于stack exchange,提问作者Meera Joseph
相关产品推荐
相关产品推荐

