如何用Django Rest Framework路由从URL捕获参数实现按用户ID列卡片
解决DRF路由实现按用户ID过滤卡片的问题
问题回顾
现有Card和User两个模型,Card通过外键owner关联User,需要实现一个视图:通过URL路径参数(如/cards/by-user-id/42/)返回指定ID用户的所有卡片。尝试用DRF的Router实现时,直接在路由前缀中写入<int:user_id>会被当作普通字符串,无法正确获取参数。
错误代码修正与解决方案
1. 基础代码修正
先修正原代码中的错误:
models.py(原代码无问题,保留)
from django.contrib.auth.models import User from django.db import models class Card(models.Model): owner = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
serializers.py(原代码无问题,保留)
from rest_framework import serializers from . import models class CardSerializer(serializers.ModelSerializer): owner = serializers.StringRelatedField() class Meta: model = models.Card fields = ['owner', 'id']
2. 视图层实现自定义路由动作
DRF默认Router不支持在注册前缀中直接添加路径参数,需通过@action装饰器定义带参数的自定义动作,同时修正原视图中的错误:
from rest_framework import viewsets from rest_framework.decorators import action from rest_framework.response import Response from . import models, serializers class CardViewSet(viewsets.ModelViewSet): queryset = models.Card.objects.all() serializer_class = serializers.CardSerializer # 定义列表级自定义动作,url_path指定路径参数规则 @action(detail=False, url_path='by-user-id/(?P<user_id>[0-9]+)') def by_user_id(self, request, user_id=None): # 过滤指定用户的卡片 cards = models.Card.objects.filter(owner__id=user_id) serializer = self.get_serializer(cards, many=True) return Response(serializer.data)
3. 路由层正确配置Router
Router仅注册基础路径,自定义动作的路径由@action的参数指定:
from django.urls import path, include from rest_framework import routers from . import views router = routers.SimpleRouter() # 注册基础Card视图集,前缀为'cards' router.register(r'cards', views.CardViewSet, basename='card') urlpatterns = [ path('', include(router.urls)), ]
配置完成后,访问/cards/by-user-id/42/即可正确返回ID为42的用户的所有卡片,user_id参数会自动传递到自定义动作方法中。
可选方案:嵌套路由(REST风格URL)
若希望URL结构更符合REST规范(如/users/42/cards/),可使用第三方包drf-nested-routers:
- 安装包:
pip install drf-nested-routers - 修改urls.py:
from django.urls import path, include from rest_framework import routers from rest_framework_nested import routers as nested_routers from . import views # 根路由注册User视图集 router = routers.SimpleRouter() router.register(r'users', views.UserViewSet, basename='user') # 基于用户路由嵌套卡片路由 user_router = nested_routers.NestedSimpleRouter(router, r'users', lookup='user') user_router.register(r'cards', views.CardViewSet, basename='user-cards') urlpatterns = [ path('', include(router.urls)), path('', include(user_router.urls)), ]
- 修改CardViewSet的查询集方法:
def get_queryset(self): user_id = self.kwargs['user_id'] return models.Card.objects.filter(owner__id=user_id)
此时访问/users/42/cards/即可获取该用户的所有卡片。
关键说明
DRF默认Router(SimpleRouter/DefaultRouter)为标准CRUD操作设计,无法直接在注册前缀中添加路径参数。通过@action装饰器可灵活定义自定义路由,或使用嵌套路由实现更规范的REST URL结构。
内容的提问来源于stack exchange,提问作者Mininux
相关产品推荐
相关产品推荐

