如何序列化含多外键的Django模型,获取用户关联课程详情
问题描述
我在Django的models.py中定义了两个模型:
from django.db import models from django.contrib.auth.models import User class Course(models.Model): name = models.CharField(max_length=100) description = models.TextField() cost = models.IntegerField() course_image = models.ImageField() def __str__(self): return self.name class PurchasedCourse(models.Model): purchased_by = models.ForeignKey(User, on_delete=models.CASCADE) purchased_course = models.ForeignKey(Course, on_delete=models.CASCADE) purchased_time = models.DateField(auto_now_add=True, blank=True) def __str__(self): return str(self.purchased_by) + ' => ' + str(self.purchased_course)
当前serializers.py内容如下:
from rest_framework import serializers from .models import * class CourseSerializer(serializers.ModelSerializer): class Meta: model = Course fields = '__all__' class PurchasedCourseSerializer(serializers.ModelSerializer): class Meta: model = PurchasedCourse fields = '__all__'
(这只是简单序列化)
我的views.py内容如下:
from django.http import HttpResponse from requests import Response from .models import * from .serializers import CourseSerializer, PurchasedCourseSerializer from rest_framework import generics, viewsets from rest_framework.authentication import BasicAuthentication, TokenAuthentication from rest_framework.permissions import IsAuthenticated class CourseViewSet(viewsets.ModelViewSet): authentication_classes = [TokenAuthentication] permission_classes = (IsAuthenticated,) queryset = Course.objects.all() serializer_class = CourseSerializer class UserRelatedCourseViewSet(viewsets.ModelViewSet): queryset = PurchasedCourse.objects.all() serializer_class = PurchasedCourseSerializer def get_queryset(self): return PurchasedCourse.objects.filter(purchased_by__username=self.request.query_params['username'])
目前接口返回的JSON格式类似{"purchased_by":5, "purchased_course":1},但我需要获取仅属于登录用户的所有完整Course对象,期望返回的JSON结构如下:
[{ "id": 1, "username": "testUsername", "course_set": [ { "id": 2, "name": "Computer Science", "description": "Test description text..." }, { "id": 5, "name": "History", "description": "Test description text..." } ] }]
解决方案
要实现需求,需要调整序列化器和视图逻辑,直接基于登录用户关联的课程数据构建响应:
1. 调整序列化器结构
因为期望返回用户+其购买的课程列表,需要创建用户相关的序列化器,嵌套课程序列化器:
修改serializers.py:
from rest_framework import serializers from django.contrib.auth.models import User from .models import Course, PurchasedCourse class CourseSerializer(serializers.ModelSerializer): class Meta: model = Course # 按需指定返回字段,不需要的字段可移除(比如course_image) fields = ['id', 'name', 'description'] class UserPurchasedCoursesSerializer(serializers.ModelSerializer): # 通过SerializerMethodField构建用户的课程列表字段 course_set = serializers.SerializerMethodField() class Meta: model = User fields = ['id', 'username', 'course_set'] def get_course_set(self, obj): # 从用户的反向关联中取出所有购买记录,提取对应的Course对象 purchased_records = obj.purchasedcourse_set.all() courses = [record.purchased_course for record in purchased_records] return CourseSerializer(courses, many=True).data
2. 修改视图逻辑
替换原有的UserRelatedCourseViewSet,直接创建获取当前登录用户购买课程的视图:
修改views.py:
from django.http import HttpResponse from rest_framework.response import Response # 注意:替换requests的Response为DRF的Response from django.contrib.auth.models import User from .serializers import UserPurchasedCoursesSerializer from rest_framework import generics, viewsets from rest_framework.authentication import BasicAuthentication, TokenAuthentication from rest_framework.permissions import IsAuthenticated # 保留原有的CourseViewSet class CourseViewSet(viewsets.ModelViewSet): authentication_classes = [TokenAuthentication] permission_classes = (IsAuthenticated,) queryset = Course.objects.all() serializer_class = CourseSerializer # 新增获取当前用户购买课程的视图 class UserPurchasedCoursesView(generics.RetrieveAPIView): authentication_classes = [TokenAuthentication] permission_classes = (IsAuthenticated,) serializer_class = UserPurchasedCoursesSerializer def get_object(self): # 直接返回当前登录的用户对象,无需依赖URL参数 return self.request.user
3. 路由配置示例
在urls.py中添加对应路由:
from django.urls import path, include from rest_framework.routers import DefaultRouter from .views import CourseViewSet, UserPurchasedCoursesView router = DefaultRouter() router.register(r'courses', CourseViewSet) urlpatterns = [ path('', include(router.urls)), # 访问该接口获取当前用户购买的课程 path('my-courses/', UserPurchasedCoursesView.as_view(), name='my-purchased-courses'), ]
访问/my-courses/接口时,即可返回期望的JSON结构,且仅包含当前登录用户购买的完整课程数据。
内容的提问来源于stack exchange,提问作者Firdavsbek Narzullaev
相关产品推荐
相关产品推荐

