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

如何在Django中结合HTTP动词使用内置类视图实现CRUD?

用Django通用视图实现RESTful风格的CRUD操作

首先要给你点个赞——你想通过同一个URL结合HTTP动词来区分CRUD操作的思路完全没问题,这是RESTful风格的标准设计,比分散的add/deleteURL更简洁规范,不用怀疑这个模式的正确性!

先说说你之前写法的问题

  1. 嵌套通用视图类无效:你在Pet类里嵌套PetListView和PetUpdateView的方式不对,Django的视图类不会自动识别并调用内部嵌套的视图,所以这些嵌套类根本没被触发,自然会返回405方法不允许。
  2. 错误调用视图函数:第二种写法里return HttpResponse(PetListView.as_view()),其实是把视图函数的对象转成字符串输出了,正确的做法是调用这个视图函数并传入request参数,也就是return PetListView.as_view()(request)。

原生Django的最佳实现方案

我们可以把通用视图的逻辑整合到一个父视图类中,按HTTP方法分配对应的通用视图处理逻辑。不过要注意:RESTful风格里,集合资源(比如所有宠物)和单个资源(比如某一只宠物)需要分开URL:

  • pets/:处理GET(查询列表)、POST(创建新宠物)
  • pets/<int:pk>/:处理GET(查询详情)、PUT(更新)、DELETE(删除)

第一步:编写视图类

from django.views.generic import ListView, CreateView, DetailView, UpdateView, DeleteView
from django.http import HttpResponseNotAllowed
from django.contrib.auth.mixins import LoginRequiredMixin
from . import models

# 处理集合资源:列表查询 + 创建
class PetListCreate(LoginRequiredMixin, View):
    login_url = "/vet/login/"

    def get(self, request, *args, **kwargs):
        # 调用ListView处理GET请求
        return ListView.as_view(
            model=models.Pet,
            context_object_name="pets",
            template_name="vet/index.html"
        )(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        # 调用CreateView处理POST请求
        return CreateView.as_view(
            model=models.Pet,
            fields=["name", "species", "age"],  # 替换成你的模型字段
            success_url="/vet/pets/"  # 创建成功后跳转回列表页
        )(request, *args, **kwargs)

    def dispatch(self, request, *args, **kwargs):
        # 拦截不支持的请求方法
        if request.method.lower() not in ["get", "post"]:
            return HttpResponseNotAllowed(["GET", "POST"])
        return super().dispatch(request, *args, **kwargs)

# 处理单个资源:详情查询 + 更新 + 删除
class PetDetailUpdateDelete(LoginRequiredMixin, View):
    login_url = "/vet/login/"

    def get(self, request, *args, **kwargs):
        # 调用DetailView处理单个资源的GET请求
        return DetailView.as_view(
            model=models.Pet,
            template_name="vet/pet_detail.html"
        )(request, *args, **kwargs)

    def put(self, request, *args, **kwargs):
        # Django的UpdateView默认只处理POST,需要把PUT请求转成POST格式
        request.method = "POST"
        # 解析PUT请求体的数据(如果是AJAX发送的JSON,需要额外处理)
        request.POST = request.PUT
        return UpdateView.as_view(
            model=models.Pet,
            fields=["name", "species", "age"],
            success_url="/vet/pets/"
        )(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        # 同理,DeleteView默认处理POST,转一下请求方法
        request.method = "POST"
        return DeleteView.as_view(
            model=models.Pet,
            success_url="/vet/pets/"
        )(request, *args, **kwargs)

    def dispatch(self, request, *args, **kwargs):
        allowed_methods = ["get", "put", "delete"]
        if request.method.lower() not in allowed_methods:
            return HttpResponseNotAllowed(["GET", "PUT", "DELETE"])
        return super().dispatch(request, *args, **kwargs)

第二步:配置URL

from django.urls import path
from . import views

urlpatterns = [
    path('login/', views.sign_in, name='login'),
    path('logout/', views.log_out, name='logout'),
    path('register/', views.register, name='register'),
    # 集合资源URL
    path('pets/', views.PetListCreate.as_view(), name="pets-list-create"),
    # 单个资源URL(带主键)
    path('pets/<int:pk>/', views.PetDetailUpdateDelete.as_view(), name="pets-detail"),
]

进阶方案:用Django REST Framework(DRF)简化代码

如果你是做API开发(比如前后端分离),DRF专门为RESTful场景设计,能大幅减少重复代码:

  1. 先安装DRF:pip install djangorestframework
  2. 编写视图集:
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated
from .models import Pet
from .serializers import PetSerializer  # 需要你自己编写序列化器

class PetViewSet(viewsets.ModelViewSet):
    queryset = Pet.objects.all()
    serializer_class = PetSerializer
    permission_classes = [IsAuthenticated]  # 对应LoginRequiredMixin
  1. 配置URL:
from rest_framework.routers import DefaultRouter
from . import views

router = DefaultRouter()
router.register(r'pets', views.PetViewSet)
urlpatterns += router.urls

DRF会自动生成以下URL并映射对应的HTTP方法:

  • pets/:GET(列表)、POST(创建)
  • pets/<pk>/:GET(详情)、PUT(更新)、DELETE(删除)

总结

  • 你的RESTful设计思路完全正确,Django完全支持这种模式
  • 原生Django需要手动把HTTP方法映射到对应的通用视图,注意处理PUT/DELETE请求的兼容性问题
  • DRF是RESTful开发的最佳搭档,能帮你省去大量重复代码

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 18:50:29