Как сериализовать модель Django с 2 или более внешними ключами?
У меня есть две модели в 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'])
Пока что я получаю следующее {"purchased_by": 5, "purchased_course": 1 } в формате JSON.
Итак, вопрос в том, как получить все объекты курса (со всеми полями), которые зависят только от вошедшего пользователя, используя PurchasedCourseSerializer?
Что-то вроде:
[{
"id": 1;
"username": "testUsername";
course_set
[
{
"id": 2;
"name": "Computer Science";
"description": "Test description text...";
}
{
"id": 5;
"name": "History";
"description": "Test description text...";
}
]
}]
Вы можете переопределить метод to_represention
в сериализаторе, чтобы изменить поведение поля во время чтения, вот так:
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, PurchasedCourse имеет FK отношение к Course, поэтому один PurchasedCouse будет иметь только один объект Course, присоединенный к нему, следовательно, вы не можете показать список как представление.
Но, если вы сериализуете из UserSerializer, то вы можете использовать следующую реализацию (через SerializerMethodField
):
class UserSerializer(...):
courses = serializer.SerializerMethodField()
def get_courses(self, obj):
return CourseSerializer(Course.objects.filter(purchased_course__purchased_by=obj), many=True).data
У вас есть два способа иметь все атрибуты внешнего ключа в сериализованном JSON.
Первое решение
Используя функцию to_representation
в вашем PurchasedCourse
сериализаторе, это будет выглядеть примерно так:
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__'
потому что первое решение дает вам более простой и ясный способ управлять тем, какие поля вам особенно нужно, чтобы этот сериализатор возвращал.
Вы также можете использовать вложенный сериализатор. Это позволяет вам явно определить, как модели внешнего ключа должны быть сериализованы, и сохраняет логику в одном месте.
Попробуйте это:
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__"
Теперь вы можете определить сериализацию каждой модели внешнего ключа в своем собственном сериализаторе и сохранить логику организованной и поддерживаемой.