在Django REST Framework中通过URL多参数获取对象的实现问题
问题:如何在URL中使用多个参数获取对象
我想通过URL中的多个参数(比如customer/3/order/1/或customer/<int:customer_pk>/order/<int:pk>/)获取对象,Shell中可以正常实现:
>>> from temp.models import Customer, Order >>> from temp.serializers import CustomerSerializer, OrderSerializer >>> q = Order.objects.filter(customer=1).get(order_number=2) >>> q.order_name 'fruit'
但在Django REST Framework视图中无法实现,当前代码如下:
views.py
from rest_framework import generics from rest_framework.views import APIView from .models import * from .serializers import * class OrderDetailView(generics.RetrieveDestroyAPIView): serializer_class = OrderSerializer def get_queryset(self): queryset = Order.objects.filter(customer_id=self.kwargs["customer_pk"]).get(order_number=self.kwargs["pk"]) return queryset
urls.py
from django.urls import path from .views import * urlpatterns = [ path('<int:customer_pk>/orders/<int:pk>/', OrderDetailView.as_view()), ]
解决方案
你的代码核心问题是:RetrieveDestroyAPIView要求get_queryset方法返回QuerySet类型对象,但你用.get()直接返回了单个Order实例,这会导致视图逻辑出错(视图后续会对查询集进行迭代或过滤操作,单个实例不支持这些方法)。
下面是两种可行的修正方式:
方式1:重写get_object方法(推荐)
直接在get_object中通过两个参数获取单个对象,这是多参数筛选单个实例的标准做法:
from rest_framework import generics from .models import Order from .serializers import OrderSerializer class OrderDetailView(generics.RetrieveDestroyAPIView): serializer_class = OrderSerializer def get_object(self): # 根据customer_pk和order_number(对应URL中的pk参数)获取对象 return Order.objects.get( customer_id=self.kwargs["customer_pk"], order_number=self.kwargs["pk"] )
方式2:修正get_queryset返回QuerySet
如果希望保留get_queryset的逻辑,需要返回过滤后的QuerySet,由视图自行根据pk(即order_number)获取对象。注意:如果Order模型的主键不是order_number,需要额外指定lookup_field:
from rest_framework import generics from .models import Order from .serializers import OrderSerializer class OrderDetailView(generics.RetrieveDestroyAPIView): serializer_class = OrderSerializer # 指定用order_number作为查找字段,对应URL中的pk参数 lookup_field = "order_number" def get_queryset(self): # 返回指定customer的所有订单QuerySet return Order.objects.filter(customer_id=self.kwargs["customer_pk"])
可选:优化URL路径
为了让URL语义更清晰,可以给路径加上customer/前缀:
from django.urls import path from .views import OrderDetailView urlpatterns = [ path('customer/<int:customer_pk>/orders/<int:pk>/', OrderDetailView.as_view()), ]
内容的提问来源于stack exchange,提问作者mRezaAnanta
相关产品推荐
相关产品推荐

