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

如何为Django的ModelViewSet类应用HTML模板?求助

解决Django ProductViewSet渲染HTML模板的问题

核心原因

ModelViewSet 是为REST API设计的默认组件,默认返回JSON/XML格式的序列化数据,而非HTML模板。要实现HTML渲染,需要修改视图的响应逻辑。


方案一:重写指定动作方法返回模板响应

直接重写list(列表页)和retrieve(详情页)方法,替换默认的API响应为TemplateResponse:

from django.template.response import TemplateResponse
from rest_framework import viewsets

class ProductViewSet(viewsets.ModelViewSet): 
    queryset = Product.objects.prefetch_related('images').all()
    serializer_class = ProductSerializer
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_class = ProductFilter
    pagination_class = DefaultPagination
    permission_classes = [IsAdminOrReadOnly]
    search_fields = ['title', 'description']
    ordering_fields = ['unit_price', 'last_update']

    def list(self, request, *args, **kwargs):
        # 执行原有过滤、分页逻辑
        queryset = self.filter_queryset(self.get_queryset())
        page = self.paginate_queryset(queryset)
        
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            # 分页场景下把数据和分页信息传入模板
            return TemplateResponse(request, 'products/product_list.html', {
                'products': serializer.data,
                'pagination': self.paginator.get_paginated_response(serializer.data)
            })
        
        serializer = self.get_serializer(queryset, many=True)
        return TemplateResponse(request, 'products/product_list.html', {
            'products': serializer.data
        })

    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return TemplateResponse(request, 'products/product_detail.html', {
            'product': serializer.data
        })

方案二:结合TemplateResponseMixin简化代码

通过混入Django的TemplateResponseMixin,统一管理模板路径和响应渲染逻辑:

from django.views.generic.base import TemplateResponseMixin
from rest_framework import viewsets

class ProductViewSet(TemplateResponseMixin, viewsets.ModelViewSet): 
    queryset = Product.objects.prefetch_related('images').all()
    serializer_class = ProductSerializer
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_class = ProductFilter
    pagination_class = DefaultPagination
    permission_classes = [IsAdminOrReadOnly]
    search_fields = ['title', 'description']
    ordering_fields = ['unit_price', 'last_update']
    template_name = 'products/product_list.html'

    def get_template_names(self):
        # 根据动作返回对应模板
        if self.action == 'retrieve':
            return ['products/product_detail.html']
        return [self.template_name]

    def render_to_response(self, context, **response_kwargs):
        # 把序列化数据注入模板上下文
        if self.action == 'list':
            queryset = self.filter_queryset(self.get_queryset())
            page = self.paginate_queryset(queryset)
            if page is not None:
                context['products'] = self.get_serializer(page, many=True).data
                context['pagination'] = self.paginator.get_paginated_response(context['products'])
            else:
                context['products'] = self.get_serializer(queryset, many=True).data
        elif self.action == 'retrieve':
            context['product'] = self.get_serializer(self.get_object()).data
        return super().render_to_response(context, **response_kwargs)

关键注意事项

  1. 确保模板文件路径正确:将product_list.html和product_detail.html放在项目的templates/products/目录下,Django能自动识别。
  2. 同时支持API和HTML:可通过判断请求的Accept头实现多格式响应,示例:
    def list(self, request, *args, **kwargs):
        if request.accepted_renderer.format == 'html':
            # 返回模板响应逻辑
            ...
        else:
            # 保留默认API JSON响应
            return super().list(request, *args, **kwargs)
    
  3. 原有逻辑不受影响:过滤、分页、权限控制等配置依然生效,仅修改了响应渲染环节。

内容的提问来源于stack exchange,提问作者amjad

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 22:35:22