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

Django 4.1添加路径参数后出现404路径不匹配问题求助

Django DRF 非主键字段路由过滤404问题解决

问题背景

使用Django 4.1和DRF开发,现有Product模型如下:

class Product(models.Model):
    product_id = models.CharField(
        blank=False,
        null=False,
        max_length=50,
        verbose_name="stock ID",
    )
    name = models.CharField(max_length=30, blank=False)
    price = models.IntegerField(blank=False)
    availability = models.IntegerField(verbose_name="product availability")
    timestamp = models.DateTimeField(auto_now=True)

其中product_id为非主键字段,同一product_id对应多条产品记录(不同时间/价格)。

配置路由后,调用接口http://127.0.0.1:8000/api/product/2048cf23-6bb2-4532-a140-b13dd1361c4/时出现404错误。尝试过<str:product_id>、<uuid:product_id>等路径转换器均无效;仅将路由改为router.register("product", productViewset, basename="productPrice")时能正常访问,但无法通过product_id过滤查询集。

错误日志显示生成的路由存在格式问题(如^product/<slug:product_id>//$),当前请求路径无法匹配任何规则。

错误原因

DRF的DefaultRouter会自动为注册的ViewSet生成list(列表)和detail(详情)路由,detail路由默认基于模型主键pk。你在router.register的prefix中直接添加<slug:product_id>,会导致:

  1. 生成的路由出现多余斜杠(如product/<slug:product_id>//),路径格式不符合请求URL;
  2. product_id被当作路由前缀的一部分,无法被ViewSet识别为查询参数;
  3. 默认的detail路由仍要求传入pk参数,与你期望的按product_id过滤逻辑冲突。

解决方案

方案一:通过查询参数过滤(最简单)

保持基础路由配置不变,在ViewSet中重写get_queryset方法,通过URL查询参数过滤结果:

  1. 路由配置(product/urls.py):
router = routers.DefaultRouter()
router.register("product", productViewset, basename="productPrice")
urlpatterns = [
    path("", include(router.urls)),
]
  1. ViewSet代码:
from rest_framework import viewsets
from .models import Product
from .serializers import ProductSerializer

class ProductViewSet(viewsets.ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer

    def get_queryset(self):
        queryset = super().get_queryset()
        # 获取URL中的product_id查询参数
        product_id = self.request.query_params.get("product_id")
        if product_id:
            queryset = queryset.filter(product_id=product_id)
        return queryset

访问方式:http://127.0.0.1:8000/api/product/?product_id=2048cf23-6bb2-4532-a140-b13dd1361c4

方案二:自定义ViewSet动作(匹配期望路径)

通过DRF的@action装饰器自定义路由,实现/api/product/{product_id}/格式的路径访问:

  1. 路由配置同方案一;

  2. ViewSet代码:

from rest_framework import viewsets, response
from rest_framework.decorators import action
from .models import Product
from .serializers import ProductSerializer

class ProductViewSet(viewsets.ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer

    # 自定义action,detail=False表示非详情路由(返回多条记录)
    @action(detail=False, methods=["get"], url_path=r"(?P<product_id>[^/.]+)")
    def by_product_id(self, request, product_id=None):
        # 按product_id过滤查询集
        queryset = self.queryset.filter(product_id=product_id)
        serializer = self.get_serializer(queryset, many=True)
        return response.Response(serializer.data)

访问方式:http://127.0.0.1:8000/api/product/2048cf23-6bb2-4532-a140-b13dd1361c4/,与你期望的路径完全匹配。

方案三:手动配置路由(脱离DefaultRouter)

如果不需要ViewSet的默认路由,可以直接手动定义路径和视图:

  1. product/urls.py配置:
from django.urls import path
from .views import ProductByIDView

urlpatterns = [
    path("product/<str:product_id>/", ProductByIDView.as_view(), name="product-by-id"),
    # 可添加其他路由
]
  1. 视图代码:
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Product
from .serializers import ProductSerializer

class ProductByIDView(APIView):
    def get(self, request, product_id):
        products = Product.objects.filter(product_id=product_id)
        serializer = ProductSerializer(products, many=True)
        return Response(serializer.data)

访问方式同方案二,路径完全匹配。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 19:15:31