如何序列化带有2个或更多外键的Django模型?

huangapple go评论55阅读模式
英文:

How to serialize Django model with 2 or more foreign keys?

问题

我有两个模型在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 }。

因此,问题是如何使用PurchasedCourseSerializer获取所有依赖于已登录用户的Course对象(包括所有字段)?

类似以下内容:

[
  {
    "id": 1,
    "username": "testUsername",
    "course_set": [
      {
        "id": 2,
        "name": "Computer Science",
        "description": "Test description text..."
      },
      {
        "id": 5,
        "name": "History",
        "description": "Test description text..."
      }
    ]
  }
]
英文:

I have two models in 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)

My serializers.py so far:

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__'

(it is just simple serializing)

My 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'])

What I am getting so far is like { "purchased_by": 5, "purchased_course": 1 } in JSON format.

So, question is how to get all Course objects(with all fields) that depends only on logged in user using PurchasedCourseSerializer?

Something like:

[{
  "id": 1;
  "username": "testUsername";
      course_set
               [
                  {
                     "id": 2;
                     "name": "Computer Science";
                     "description": "Test description text...";
                  }
                  {
                     "id": 5;
                     "name": "History";
                     "description": "Test description text...";
                  }
               ]
}]

答案1

得分: 1

你可以在序列化器中覆盖to_representation方法来更改字段在读取时的行为,就像这样:

class PurchasedCourseSerializer(serializers.ModelSerializer):
    username = serializers.CharField(source="purchased_by.username")

    def to_representation(self, obj):
        self.fields['purchased_course'] = CourseSerializer(obj.purchased_course)
        return super().to_representation(obj)

    class Meta:
        model = PurchasedCourse
        fields = '__all__'

顺便说一下,PurchasedCourse与Course之间有一个外键关系,因此每个PurchasedCourse只附加一个Course对象,因此不能像列表一样显示。

但是,如果你要从UserSerializer进行序列化,可以使用以下实现(通过SerializerMethodField):

class UserSerializer(...):
    courses = serializers.SerializerMethodField()
    
    def get_courses(self, obj):
        return CourseSerializer(Course.objects.filter(purchased_course__purchased_by=obj), many=True).data
英文:

You can override the to_represention method in the serializer to change behavior of the field during reading, like this:

class PurchasedCourseSerializer(serializers.ModelSerializer):
    username = serializers.CharField(source="purchased_by.username")

    def to_representation(self, obj):
       self.fields['purchased_course'] = CourseSerializer(obj.purchased_course )
       return super().to_representation(obj)
    class Meta:
        model = PurchasedCourse
        fields = '__all__'

FYI, the PurchasedCourse has a FK relation to Course, so one PurchasedCouse will have only one Course object attached to it, hence you can not show the list like representation.

But, if you are serializing from UserSerializer, then you can use the following implementation (through SerializerMethodField):

class UserSerializer(...):
    courses = serializer.SerializerMethodField()

    def get_courses(self, obj):
      return CourseSerializer(Course.objects.filter(purchased_course__purchased_by=obj), many=True).data

答案2

得分: 1

第一种解决方案:

使用PurchasedCourse序列化器中的to_representation函数,看起来像这样:

class PurchasedCourseSerializer(serializers.ModelSerializer):
    class Meta:
        model = PurchasedCourse
        fields = "__all__"

    def to_representation(self, instance):
        rep = super().to_representation(instance)
        rep["purchased_course"] = CourseSerializer(instance.purchased_course).data
        rep["purchased_by"] = UserSerializer(instance.purchased_by).data

        return rep

这基本上使用外键的序列化器来表示这些字段,使用它们的完整数据或您在这些序列化器中指定的字段。

第二种解决方案:

使用嵌套序列化器基本上与第一种解决方案相同,但可能更冗长,如下所示:

class PurchasedCourseSerializer(serializers.ModelSerializer):
    purchased_course = CourseSerializer()
    purchased_by = UserSerializer()

    class Meta:
        model = PurchasedCourse
        fields = '__all__'

因为第一种解决方案提供了更容易和更清晰的方式来管理此序列化器要返回哪些字段。

英文:

You have basically two ways to have all attributes of a foreign key in your serialized JSON.

First solution

Using a to_representation function in your PurchasedCourse serializer, it would look something like this:

class PurchasedCourseSerializer(serializers.ModelSerializer):
    class Meta:
        model = PurchasedCourse
        fields = "__all__"

    def to_representation(self, instance):
        rep = super().to_representation(instance)
        rep["purchased_course"] = CourseSerializer(instance.purchased_course).data
        rep["purchased_by"] = UserSerializer(instance.purchased_by).data

        return rep

and this basically uses the serializers of your foreign keys to represent those fields with their full data or whatever fields you specify in those serializers.


Second solution

Using nested serializers is basically the same as the first solution but in a more clanky way - at least for me -

class PurchasedCourseSerializer(serializers.ModelSerializer):
    purchased_course = CourseSerializer()
    purchased_by = UserSerializer()

    class Meta:
        model = PurchasedCourse
        fields = '__all__'

because the first solution gives you an easier and more clear way to manage which fields you particularly need this serializer to return.

答案3

得分: 1

以下是翻译好的部分:

You can also use Nested Serializer. This allows you to explicitly define how the foreign key models should be serialized, and it keeps the logic in one place.

Try this:

class CourseSerializer(serializers.ModelSerializer):
    class Meta:
        model = Course
        fields = "all"

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = "all"

class PurchasedCourseSerializer(serializers.ModelSerializer):
    purchased_course = CourseSerializer()
    purchased_by = UserSerializer()

    class Meta:
        model = PurchasedCourse
        fields = "all"

Now, you can define the serialization of each foreign key model in its own serializer, and keep the logic organized and maintainable.

英文:

You can also use Nested Serializer. This allows you to explicitly define how the foreign key models should be serialized, and it keeps the logic in one place.

Try this:

class CourseSerializer(serializers.ModelSerializer):
    class Meta:
        model = Course
        fields = "__all__"

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = "__all__"

class PurchasedCourseSerializer(serializers.ModelSerializer):
    purchased_course = CourseSerializer()
    purchased_by = UserSerializer()

    class Meta:
        model = PurchasedCourse
        fields = "__all__"

Now, you can define the serialization of each foreign key model in its own serializer, and keep the logic organized and maintainable.

huangapple
  • 本文由 发表于 2023年2月8日 21:37:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/75386610.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定