如何为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)
关键注意事项
- 确保模板文件路径正确:将
product_list.html和product_detail.html放在项目的templates/products/目录下,Django能自动识别。 - 同时支持API和HTML:可通过判断请求的
Accept头实现多格式响应,示例:def list(self, request, *args, **kwargs): if request.accepted_renderer.format == 'html': # 返回模板响应逻辑 ... else: # 保留默认API JSON响应 return super().list(request, *args, **kwargs) - 原有逻辑不受影响:过滤、分页、权限控制等配置依然生效,仅修改了响应渲染环节。
内容的提问来源于stack exchange,提问作者amjad
相关产品推荐
相关产品推荐

