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

如何在Django REST Framework中将多API视图绑定到单个端点?

问题描述

我有一个名为Article的模型,以及多个针对该模型的API视图,这些视图按不同用途拆分(例如用ArticleUpdateAPI类处理UPDATE方法,ArticleDeleteAPI类处理DELETE方法等)。目前在urls.py中,它们对应不同的端点(如article/pk/update、/article/pk/delete等)。

我知道这种端点设计不是最佳实践,想把这些视图绑定到单个URL,用不同的类处理不同的HTTP方法。请问是否可行?具体该怎么实现?

现有代码如下:

ArticleAPI.py

class ArticlePostAPI(generics.CreateAPIView):
    serializer_class = ArticleSerializer
    permission_classes = [
        permissions.IsAuthenticatedOrReadOnly
    ]

    def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response({
            "comment": CommentSerializer.data
        }, status=201)


class ArticleRetrieveAPI(generics.RetrieveAPIView):
    serializer_class = ArticleSerializer
    queryset = Article.objects.all()
    permission_classes = [
        permissions.AllowAny
    ]


class ArticleListAPI(generics.ListAPIView):
    serializer_class = ArticleSerializer
    queryset = Article.objects.order_by('number', 'headline')
    permission_classes = [
        permissions.AllowAny
    ]


class ArticleUpdateAPI(generics.UpdateAPIView):
    serializer_class = ArticleSerializer
    queryset = Article.objects.all()
    permission_classes = [
        permissions.IsAuthenticated
    ]

    lookup_field = 'pk'

    def update(self, request, *args, **kwargs):
        instance = self.get_object()

        if request.user != instance.author:
            return Response({
               "errors": "Logged in user and author must be same"
            }, status=403)

        serializer = self.get_serializer(instance, data=request.data, partial=True)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        return Response(serializer.data)

urls.py

urlpatterns = [
    ...

    # Article API

    path('article/post/', ArticlePostAPI.as_view(), name='article_creation'),
    path('article/<int:pk>/', ArticleRetrieveAPI.as_view(), name='article_retrieve'),
    path('article/', ArticleListAPI.as_view(), name='article_list'),
    path('article/<int:pk>/update/', ArticleUpdateAPI.as_view(), name='article_update'),
]
解决方案

完全可行,这是RESTful API的标准设计思路——同一个URL根据不同HTTP方法执行不同操作。在Django REST Framework中有两种常用实现方式:

方式一:使用DRF内置组合视图类

DRF提供了预定义的组合视图类,比如ListCreateAPIView(处理GET列表、POST创建)、RetrieveUpdateDestroyAPIView(处理GET详情、PUT/PATCH更新、DELETE删除),可直接复用合并视图,减少重复代码。

修改后视图代码

from rest_framework import generics, permissions
from rest_framework.response import Response

class ArticleListCreateAPI(generics.ListCreateAPIView):
    serializer_class = ArticleSerializer
    permission_classes = [permissions.AllowAny]
    queryset = Article.objects.order_by('number', 'headline')

    # 保留原自定义POST逻辑
    def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response({
            "comment": CommentSerializer.data
        }, status=201)

class ArticleRetrieveUpdateAPI(generics.RetrieveUpdateAPIView):
    serializer_class = ArticleSerializer
    queryset = Article.objects.all()
    lookup_field = 'pk'

    # 根据请求方法动态设置权限
    def get_permissions(self):
        if self.request.method == 'GET':
            return [permissions.AllowAny()]
        return [permissions.IsAuthenticated()]

    # 保留原自定义更新权限校验逻辑
    def update(self, request, *args, **kwargs):
        instance = self.get_object()

        if request.user != instance.author:
            return Response({
               "errors": "Logged in user and author must be same"
            }, status=403)

        serializer = self.get_serializer(instance, data=request.data, partial=True)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        return Response(serializer.data)

修改后urls.py

urlpatterns = [
    ...

    # Article API
    path('article/', ArticleListCreateAPI.as_view(), name='article_list_create'),
    path('article/<int:pk>/', ArticleRetrieveUpdateAPI.as_view(), name='article_retrieve_update'),
]

绑定后对应关系:

  • GET /article/ → 列表查询(原ArticleListAPI逻辑)
  • POST /article/ → 创建文章(原ArticlePostAPI逻辑)
  • GET /article/<pk>/ → 详情查询(原ArticleRetrieveAPI逻辑)
  • PUT/PATCH /article/<pk>/ → 更新文章(原ArticleUpdateAPI逻辑)

方式二:自定义视图类继承APIView

如果需要更灵活的控制,可直接继承APIView,手动绑定不同HTTP方法的处理函数,将原多视图逻辑整合到单个类中。

示例代码

from rest_framework.views import APIView
from rest_framework import permissions, status
from rest_framework.response import Response

class ArticleAPI(APIView):
    def get_permissions(self):
        if self.request.method in ['GET']:
            return [permissions.AllowAny()]
        elif self.request.method in ['POST', 'PUT', 'PATCH']:
            return [permissions.IsAuthenticated()]
        return super().get_permissions()

    # 处理GET请求:区分列表/详情
    def get(self, request, pk=None):
        if pk:
            try:
                article = Article.objects.get(pk=pk)
            except Article.DoesNotExist:
                return Response({"error": "Article not found"}, status=status.HTTP_404_NOT_FOUND)
            serializer = ArticleSerializer(article)
            return Response(serializer.data)
        else:
            articles = Article.objects.order_by('number', 'headline')
            serializer = ArticleSerializer(articles, many=True)
            return Response(serializer.data)

    # 处理POST创建请求
    def post(self, request):
        serializer = ArticleSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response({
            "comment": CommentSerializer.data
        }, status=status.HTTP_201_CREATED)

    # 统一处理PUT/PATCH更新请求
    def put(self, request, pk):
        return self.update(request, pk)

    def patch(self, request, pk):
        return self.update(request, pk, partial=True)

    def update(self, request, pk, partial=False):
        try:
            article = Article.objects.get(pk=pk)
        except Article.DoesNotExist:
            return Response({"error": "Article not found"}, status=status.HTTP_404_NOT_FOUND)

        if request.user != article.author:
            return Response({
               "errors": "Logged in user and author must be same"
            }, status=status.HTTP_403_FORBIDDEN)

        serializer = ArticleSerializer(article, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(serializer.data)

修改后urls.py

urlpatterns = [
    ...

    # Article API
    path('article/', ArticleAPI.as_view(), name='article_list_create'),
    path('article/<int:pk>/', ArticleAPI.as_view(), name='article_detail_update'),
]

这种方式自由度更高,适合需要在单个类中统一处理多类逻辑的场景。

内容的提问来源于stack exchange,提问作者Sanyokk

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 15:30:50