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

Django Rest Framework自定义ViewSet实现slug+uuid组合ID访问

实现Slug+UUID组合路径访问Django DRF资源的方案

1. 自动生成Slug字段

解决POST创建资源时自动根据name生成slug的需求,推荐在模型save方法中直接处理:

from django.db import models
import uuid
from django.utils.text import slugify

class Product(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    slug = models.SlugField(max_length=250, unique_for_date='published')
    name = models.CharField(max_length=250)
    # 需补充published字段,匹配slug的unique_for_date规则,不需要该规则可直接删除slug的对应参数
    published = models.DateTimeField(auto_now_add=True)

    def save(self, *args, **kwargs):
        # 仅创建时生成slug,需要更新name时同步更新slug可去掉if判断
        if not self.slug:
            self.slug = slugify(self.name)
        super().save(*args, **kwargs)

    def get_absolute_url(self):
        # 路由命名对应后续配置的products:product-detail
        return reverse('products:product-detail', kwargs={'uuid': self.id, 'slug': self.slug})

同时需要在序列化器中将slug设为只读,避免POST请求要求传入该参数:

from rest_framework import serializers
from .models import Product

class ProductSerializer(serializers.ModelSerializer):
    class Meta:
        model = Product
        fields = ['id', 'slug', 'name', 'published']
        extra_kwargs = {
            'slug': {'read_only': True}
        }

可选优化:如果需要支持中文name生成slug,可使用pypinyin库将中文转为拼音后再生成slug,或者直接使用django-autoslug第三方模块自动处理slug生成逻辑。

2. 自定义路由配置

替换DefaultRouter默认生成的详情路由,手动添加符合<slug>-<uuid>格式的detail路由:

from rest_framework.routers import DefaultRouter
from django.urls import path
from .views import ProductViewSet

app_name = 'products'

router = DefaultRouter()
# 注册基础路由,自动生成列表查询、资源创建等通用路由
router.register('', ProductViewSet, basename='product')

urlpatterns = [
    # 自定义详情页路由,绑定对应操作
    path('<slug:slug>-<uuid:uuid>/', ProductViewSet.as_view({
        'get': 'retrieve',
        'put': 'update',
        'patch': 'partial_update',
        'delete': 'destroy'
    }), name='product-detail'),
] + router.urls

注:手动添加的路由优先级高于router自动生成的默认detail路由,不会出现路径冲突。Django的路径转换器会从右往左匹配,先识别符合UUID格式的部分,剩余前缀自动归为slug,无需担心slug中包含横杠的问题。

3. 重写ViewSet的get_object方法

修改原有的ModelViewSet逻辑,从请求路径中提取slug和uuid两个参数查询资源:

from rest_framework import viewsets, generics
from .models import Product
from .serializers import ProductSerializer

# 原类名ProductList语义不准确,调整为ProductViewSet更符合功能定位
class ProductViewSet(viewsets.ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer

    def get_object(self):
        # 从路径参数中提取slug和uuid
        slug = self.kwargs.get('slug')
        product_uuid = self.kwargs.get('uuid')
        # 双参数查询保证资源正确性,避免仅用UUID查询时slug被篡改仍能访问的问题
        return generics.get_object_or_404(Product, id=product_uuid, slug=slug)

内容的提问来源于stack exchange,提问作者some nooby questions

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 19:48:05