如何在Django REST ModelViewSet中实现多GET请求与不同URL?
new_arrivals Endpoint Great question! You don’t need to create a separate viewset or register a new URL—you can seamlessly add the new_arrivals functionality to your existing BookViewSet using custom actions in Django REST Framework (DRF). This keeps your code organized and lets you reuse the viewset’s existing logic (like permissions, serialization, and filtering).
Step 1: Add the Custom Action to BookViewSet
Use DRF’s @action decorator to define the new_arrivals method directly in your BookViewSet. This decorator tells DRF to treat this method as a separate endpoint, and it will automatically generate the correct URL pattern for you.
Here’s the updated code for your viewset:
from rest_framework import viewsets from rest_framework.decorators import action from rest_framework.response import Response from django.utils import timezone from datetime import timedelta from .models import Book from .serializers import BookSerializer # 替换成你的序列化器 class BookViewSet(viewsets.ModelViewSet): serializer_class = BookSerializer def get_queryset(self): # 你的现有逻辑:获取指定图书馆的图书,并支持筛选 library_id = self.kwargs.get('library_id') queryset = Book.objects.filter(library_id=library_id) # 处理genre、status、author筛选 genre = self.request.query_params.get('genre') if genre: queryset = queryset.filter(genre=genre) status = self.request.query_params.get('status') if status: queryset = queryset.filter(status=status) author = self.request.query_params.get('author') if author: queryset = queryset.filter(author__icontains=author) return queryset @action(detail=False, methods=['get'], url_path='new_arrivals') def new_arrivals(self, request, library_id=None): # 计算近两周的时间阈值 two_weeks_ago = timezone.now() - timedelta(weeks=2) # 复用get_queryset()来继承现有筛选逻辑,再叠加时间过滤 queryset = self.get_queryset().filter(created_at__gte=two_weeks_ago) # 注意:如果你的Book模型用的是其他字段记录新增时间(比如added_at),替换成对应的字段名 serializer = self.get_serializer(queryset, many=True) return Response(serializer.data)
Step 2: No Changes Needed to Your URL Registration
Your existing router registration will automatically handle the new endpoint. The @action decorator with url_path='new_arrivals' will generate the exact URL you had before: api/books/<library_id>/new_arrivals.
DRF’s router builds the URL pattern by appending the url_path value to the base path of your viewset’s registered URL. So you can keep using:
router.register(r'books/(?P<library_id>[0-9]+)', BookViewSet, base_name='books')
Why This Approach Works
- Code Reusability: The
new_arrivalsmethod usesself.get_queryset(), so it automatically inherits your existing genre/status/author filtering logic. You don’t have to duplicate code! - Consistency: It uses the same serializer, permissions, and pagination settings as your main BookViewSet, keeping your API behavior consistent.
- Less Maintenance: No extra URLs or view classes to manage—everything lives in one place.
Key Notes
- Make sure your
Bookmodel has a datetime field that tracks when the book was added (likecreated_atoradded_at). Adjust the filter field innew_arrivalsto match your actual field name. - If you need different permissions or serialization for the
new_arrivalsendpoint, you can override those in the@actiondecorator (e.g.,permission_classes=[IsAdminUser]). - The
detail=Falseparameter tells DRF this action doesn’t operate on a single instance of Book (it’s a list action), which matches your use case of returning multiple new books.
内容的提问来源于stack exchange,提问作者Thinker

