Django router配置带路径参数路由访问时返回404错误
DRF 内置 Router 的 register 方法第一个入参是资源路由前缀,仅用来声明当前ViewSet对应的根路径,不支持直接在这个前缀字符串里编写<int:xxx>格式的路径参数。
Router 会自动基于传入的前缀,结合ViewSet内置的标准action(list/retrieve/create/update/destroy)、以及@action装饰器注册的自定义action生成全套路由规则,硬写在前缀里的路径参数不会被路由解析器识别,所以请求会直接返回404。
路由注册统一使用不带路径参数的前缀即可:
router.register(r'get-customers', GetCustomersViewSet, basename='get-customers')
根据实际接口场景选择对应实现:
场景1:接口需求是获取number字段等于传入值的单个客户(类REST标准单资源查询接口)
直接在ViewSet中指定lookup_field参数,把默认匹配的pk路径参数改成number即可,Router会自动生成/get-customers/<number>/格式的路由:from rest_framework import viewsets from .models import Customer from .serializers import CustomerSerializer class GetCustomersViewSet(viewsets.ReadOnlyModelViewSet): queryset = Customer.objects.all() serializer_class = CustomerSerializer # 指定路径匹配的字段为number lookup_field = 'number'配置完成后访问
http://127.0.0.1:9090/get-customers/20/(注意DRF默认要求路径末尾带斜杠,未开启斜杠豁免的话不加会报404),就会自动进入ViewSet的retrieve方法,返回number=20对应的客户数据。场景2:接口需求是根据传入的数字参数返回对应数量的客户列表(自定义非标准接口)
不要把参数写在路由前缀里,通过@action装饰器注册自定义路由,在装饰器的url_path中声明路径参数即可:from rest_framework import viewsets from rest_framework.decorators import action from rest_framework.response import Response from .models import Customer from .serializers import CustomerSerializer class GetCustomersViewSet(viewsets.ViewSet): @action(detail=False, methods=['get'], url_path=r'(?P<number>\d+)') def get_customers_by_count(self, request, number=None): # 返回前number条客户数据 customers = Customer.objects.all()[:int(number)] serializer = CustomerSerializer(customers, many=True) return Response(serializer.data)这里
url_path直接写正则匹配数字,最终生成的路由就是/get-customers/<number>/,和预期的路径格式完全一致,访问http://127.0.0.1:9090/get-customers/20/就能正常返回对应数据。
补充排查点:Django默认开启
APPEND_SLASH=True配置,会自动校验请求路径的末尾斜杠,如果访问路径没加末尾斜杠,要么会被301重定向到带斜杠的地址,要么直接返回404,测试时需要注意路径格式。
内容的提问来源于stack exchange,提问作者lewis machilika

