如何在DRF中通过关联Deck ID获取Progress对象并实现CRUD
解决方案
一、通过Deck ID获取Progress对象的实现
方法1:修改ViewSet的Lookup逻辑(推荐)
直接调整ProgressViewSet的查找规则,让它通过deck_id定位Progress对象,这样就能通过http://localhost/api/v1/progress/<deck_id>/访问目标资源:
from django.shortcuts import get_object_or_404 from rest_framework import viewsets class ProgressViewSet(viewsets.ModelViewSet): queryset = Progress.objects.all() serializer_class = ProgressSerializer permission_classes = (OwnerOrAdminProgress,) # 将查找字段指定为deck_id lookup_field = 'deck_id' http_method_names = ['get', 'post', 'patch', 'delete'] def get_object(self): # 重写对象获取逻辑,通过deck_id匹配Progress deck_id = self.kwargs.get(self.lookup_field) return get_object_or_404(Progress, deck_id=deck_id)
配置完成后,访问/api/v1/progress/1/即可获取对应deck_id=1的Progress对象,同时不影响原有CRUD接口的正常运行。
方法2:自定义ViewSet动作
如果不想修改默认查找规则,可以添加一个独立的自定义动作,通过专属端点获取:
from rest_framework.decorators import action from rest_framework.response import Response from django.shortcuts import get_object_or_404 class ProgressViewSet(viewsets.ModelViewSet): # 保留原有代码... @action(detail=False, url_path='by-deck/(?P<deck_id>[^/.]+)') def by_deck(self, request, deck_id=None): progress = get_object_or_404(Progress, deck_id=deck_id) serializer = self.get_serializer(progress) return Response(serializer.data)
此时访问/api/v1/progress/by-deck/1/即可获取对应Deck的Progress对象。
二、完善完整CRUD功能
1. 修复模型必填字段
补充Progress模型中OneToOneField的on_delete参数(Django强制要求):
class Progress(models.Model): deck = models.OneToOneField(Deck, on_delete=models.CASCADE) # 添加on_delete参数 progress = models.JSONField(null=True)
2. 优化序列化器
如果希望返回Deck的详细信息而非仅ID,可以使用嵌套序列化器,同时支持创建时传入deck_id:
from rest_framework import serializers from .models import Deck, Progress class DeckSerializer(serializers.ModelSerializer): class Meta: model = Deck fields = ('id', 'name', 'owner') class ProgressSerializer(serializers.ModelSerializer): deck = DeckSerializer(read_only=True) # 嵌套返回Deck详情 deck_id = serializers.PrimaryKeyRelatedField(queryset=Deck.objects.all(), write_only=True) # 创建时传入deck_id class Meta: model = Progress fields = ('id', 'deck', 'deck_id', 'progress') def create(self, validated_data): # 剥离deck_id并关联到Progress实例 deck_id = validated_data.pop('deck_id') return Progress.objects.create(deck_id=deck_id, **validated_data)
3. 完善权限控制
确保OwnerOrAdminProgress权限类正确校验用户权限,示例实现:
from rest_framework import permissions class OwnerOrAdminProgress(permissions.BasePermission): def has_object_permission(self, request, view, obj): # 管理员拥有所有操作权限 if request.user.is_staff: return True # 仅允许Deck的所有者访问对应的Progress return obj.deck.owner == request.user
4. CRUD接口验证
配置完成后,以下接口均能正常工作:
- 创建Progress:POST
/api/v1/progress/,请求体传入deck_id和progress字段 - 获取单个Progress:GET
/api/v1/progress/<deck_id>/(方法1)或GET/api/v1/progress/by-deck/<deck_id>/(方法2) - 更新Progress:PATCH
/api/v1/progress/<deck_id>/,传入需要修改的progress字段 - 删除Progress:DELETE
/api/v1/progress/<deck_id>/ - 获取所有Progress(限管理员/有权限用户):GET
/api/v1/progress/
三、其他可选方案
- 通过Deck接口嵌套获取:在
DeckViewSet中添加自定义动作,实现访问/api/v1/decks/<deck_id>/progress/直接获取关联的Progress对象 - 使用过滤参数:借助
django-filter库,通过/api/v1/progress/?deck_id=1的方式过滤获取目标Progress
内容的提问来源于stack exchange,提问作者CarloDiPalma
相关产品推荐
相关产品推荐

