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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 14:15:34