如何在Django中结合HTTP动词使用内置类视图实现CRUD?
用Django通用视图实现RESTful风格的CRUD操作
首先要给你点个赞——你想通过同一个URL结合HTTP动词来区分CRUD操作的思路完全没问题,这是RESTful风格的标准设计,比分散的add/deleteURL更简洁规范,不用怀疑这个模式的正确性!
先说说你之前写法的问题
- 嵌套通用视图类无效:你在
Pet类里嵌套PetListView和PetUpdateView的方式不对,Django的视图类不会自动识别并调用内部嵌套的视图,所以这些嵌套类根本没被触发,自然会返回405方法不允许。 - 错误调用视图函数:第二种写法里
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场景设计,能大幅减少重复代码:
- 先安装DRF:
pip install djangorestframework - 编写视图集:
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
- 配置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
相关产品推荐
相关产品推荐

