Django DRF通用视图实现订单列详创时详情接口返回列表异常
问题核心
你给DRF通用视图的as_view()传入了{'get':'retrieve'}这类action字典,但通用视图(如ListAPIView、RetrieveAPIView)并不支持这种用法——只有ViewSet系列才支持通过action字典映射HTTP方法到视图方法。因此你的详情路由配置无效,访问带pk的路径时,依然调用了ListAPIView的get方法(执行列表查询逻辑),导致返回所有订单列表。
另外,你重写的get_object方法未过滤当前用户,存在越权访问其他用户订单的安全隐患。
解决方案
方案1:改用ViewSet(DRF标准推荐)
使用GenericViewSet结合对应混合类,这是DRF实现多动作视图的标准方式,支持通过action字典配置路由。
修改视图类
from rest_framework import viewsets, mixins, status from rest_framework.response import Response from rest_framework import permissions from .models import Order, Product from .serializers import addOrderSerializer, addItemSerializer, addShippingSerializer class OrderViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet): permission_classes = (permissions.IsAuthenticated,) serializer_class = addOrderSerializer # 统一过滤当前用户的订单,list/retrieve都会自动使用该查询集 def get_queryset(self): return Order.objects.filter(user=self.request.user) # 保留自定义序列化器方法 def get_item_serializer(self, *args, **kwargs): return addItemSerializer(*args, **kwargs) def get_shipping_serializer(self, *args, **kwargs): return addShippingSerializer(*args, **kwargs) # 保留原创建逻辑 def create(self, request, *args, **kwargs): user = request.user data = request.data orderItems = data.get('orderItems') if not bool(orderItems): return Response('No Order Items', status=status.HTTP_400_BAD_REQUEST) # 创建订单 orderSerializer = self.get_serializer(data=data) orderSerializer.is_valid(raise_exception=True) order = orderSerializer.save(user=user) # 创建收货地址 shippingSerializer = self.get_shipping_serializer(data=data) shippingSerializer.is_valid(raise_exception=True) shippingSerializer.save(order=order) # 创建订单项并更新库存 for item in orderItems: product = Product.objects.get(pk=item['product']) itemSerializer = self.get_item_serializer(data=item) itemSerializer.is_valid(raise_exception=True) item = itemSerializer.save(order=order, product=product) product.countInStock -= item.qty product.save() return Response(data=orderSerializer.data) # 无需手动重写retrieve/list,混合类已实现并自动用get_queryset过滤
修改路由配置
from django.urls import path from . import views urlpatterns = [ # 根路径:GET获取列表,POST创建订单 path('', views.OrderViewSet.as_view({'get': 'list', 'post': 'create'})), # 兼容原有add/路径,处理POST创建订单 path('add/', views.OrderViewSet.as_view({'post': 'create'})), # 详情路径:GET获取单个订单 path('<int:pk>/', views.OrderViewSet.as_view({'get': 'retrieve'})), ]
方案2:拆分独立视图类(保留通用视图)
如果不想改用ViewSet,可以拆分出独立的列表、创建、详情视图,避免方法冲突。
拆分视图类
from rest_framework import generics, status from rest_framework.response import Response from rest_framework import permissions from .models import Order, Product from .serializers import addOrderSerializer, addItemSerializer, addShippingSerializer # 列表视图 class OrderListView(generics.ListAPIView): permission_classes = (permissions.IsAuthenticated,) serializer_class = addOrderSerializer def get_queryset(self): return Order.objects.filter(user=self.request.user) # 创建视图 class OrderCreateView(generics.CreateAPIView): permission_classes = (permissions.IsAuthenticated,) serializer_class = addOrderSerializer def get_item_serializer(self, *args, **kwargs): return addItemSerializer(*args, **kwargs) def get_shipping_serializer(self, *args, **kwargs): return addShippingSerializer(*args, **kwargs) def create(self, request, *args, **kwargs): # 原创建逻辑,与之前一致 user = request.user data = request.data orderItems = data.get('orderItems') if not bool(orderItems): return Response('No Order Items', status=status.HTTP_400_BAD_REQUEST) orderSerializer = self.get_serializer(data=data) orderSerializer.is_valid(raise_exception=True) order = orderSerializer.save(user=user) shippingSerializer = self.get_shipping_serializer(data=data) shippingSerializer.is_valid(raise_exception=True) shippingSerializer.save(order=order) for item in orderItems: product = Product.objects.get(pk=item['product']) itemSerializer = self.get_item_serializer(data=item) itemSerializer.is_valid(raise_exception=True) item = itemSerializer.save(order=order, product=product) product.countInStock -= item.qty product.save() return Response(data=orderSerializer.data) # 详情视图 class OrderRetrieveView(generics.RetrieveAPIView): permission_classes = (permissions.IsAuthenticated,) serializer_class = addOrderSerializer def get_queryset(self): return Order.objects.filter(user=self.request.user)
修改路由配置
from django.urls import path from . import views urlpatterns = [ path('', views.OrderListView.as_view()), path('add/', views.OrderCreateView.as_view()), path('<int:pk>/', views.OrderRetrieveView.as_view()), ]
额外注意点
- 方案1中,
RetrieveModelMixin的retrieve方法会自动从get_queryset过滤当前用户订单,无需手动重写get_object,彻底避免越权问题。 - 不推荐在通用视图的
as_view()中传入action字典,这是ViewSet专属用法,容易引发方法冲突。
内容的提问来源于stack exchange,提问作者Carter James
相关产品推荐
相关产品推荐

