Django中用独立APIView类实现同一路由CRUD遇405错误如何解决?
解决Django同路由绑定多独立视图的问题
你遇到的405错误确实是Django路由匹配顺序导致的:当请求some_resource/时,会优先匹配第一个path对应的SomeResourceListView,而这个视图只实现了GET方法,自然处理不了POST请求。
要在保持视图类独立的前提下,让同一路径绑定多个视图,有两种可行方案:
方案一:利用path的methods参数(Django 2.2+支持)
直接在路由配置中指定每个path对应的HTTP方法,Django会根据请求的方法匹配对应的视图。修改后的urls.py如下:
from django.urls import path from .views import ( SomeResourceListView, SomeResourceCreateView, SomeResourceRetrieveView ) url_patterns = [ # 处理GET请求的列表接口 path( "some_resource/", SomeResourceListView.as_view(), name="some-resource-list", methods=["GET"] ), # 处理POST请求的创建接口 path( "some_resource/", SomeResourceCreateView.as_view(), name="some-resource-create", methods=["POST"] ), # 处理GET请求的详情接口 path( "some_resource/<int:pk>", SomeResourceRetrieveView.as_view(), name="some-resource-retrieve", methods=["GET"] ), ]
方案二:在as_view()中指定http_method_names
通过给视图的as_view()方法传递http_method_names参数,限制该视图实例只处理指定的HTTP方法,这样即使路由路径相同,Django也会根据方法匹配正确的视图:
from django.urls import path from .views import ( SomeResourceListView, SomeResourceCreateView, SomeResourceRetrieveView ) url_patterns = [ path( "some_resource/", SomeResourceListView.as_view(http_method_names=["get"]), name="some-resource-list" ), path( "some_resource/", SomeResourceCreateView.as_view(http_method_names=["post"]), name="some-resource-create" ), path("some_resource/<int:pk>", SomeResourceRetrieveView.as_view(), name="some-resource-retrieve"), ]
扩展:实现完整CRUD的路由配置
如果后续要添加update(PUT/PATCH)和delete(DELETE)操作,同样可以用上述方法绑定到some_resource/<int:pk>路径:
假设已创建对应的视图类:
class SomeResourceUpdateView(APIView): def put(self, request, pk, *args, **kwargs): # 全量更新逻辑 ... def patch(self, request, pk, *args, **kwargs): # 部分更新逻辑 ... class SomeResourceDeleteView(APIView): def delete(self, request, pk, *args, **kwargs): # 删除逻辑 ...
对应的路由配置:
url_patterns = [ # 列表与创建 path("some_resource/", SomeResourceListView.as_view(), name="some-resource-list", methods=["GET"]), path("some_resource/", SomeResourceCreateView.as_view(), name="some-resource-create", methods=["POST"]), # 详情、更新、删除 path("some_resource/<int:pk>", SomeResourceRetrieveView.as_view(), name="some-resource-retrieve", methods=["GET"]), path("some_resource/<int:pk>", SomeResourceUpdateView.as_view(), name="some-resource-update", methods=["PUT", "PATCH"]), path("some_resource/<int:pk>", SomeResourceDeleteView.as_view(), name="some-resource-delete", methods=["DELETE"]), ]
这两种方案都能严格保持每个视图类的单一职责,同时实现同路由多方法的绑定需求。
内容的提问来源于stack exchange,提问作者Martin Faucheux
相关产品推荐
相关产品推荐

