Django Повторное использование сериализаторов моделей Пользовательские валидаторы

У меня есть приведенный ниже файл serializer.py. У меня есть validate_semester(), присутствующий в SubjectSerializer(), я хочу повторно использовать тот же метод в классе StudentSerializer(). Любая помощь будет оценена по достоинству. validate_semester() проверяет, существует ли идентификатор семестра в таблице семестров, прежде чем присвоить его объекту Student или Subject.

from rest_framework import serializers
from .models import *
from .helpers import SEMESTER_NOT_STORED


class SemesterSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False)
    name = serializers.CharField(max_length=100, required=False)


class SubjectSerializer(serializers.ModelSerializer):
    semester = SemesterSerializer()

    class Meta:
        model = Subject
        fields = ["id", "name", "code", "semester"]

    def create(self, validated_data):
        semester = validated_data.pop("semester")
        serializer = SemesterSerializer(semester)
        subject = Subject.objects.create(
            semester_id=serializer.data["id"], **validated_data
        )
        return subject

    def validate_semester(self, value):
        id = value.get("id")
        try:
            Semester.objects.get(id=id)
        except Semester.DoesNotExist:
            raise serializers.ValidationError({"id":[SEMESTER_NOT_STORED.format(id)]})
        return value


class SemesterSubjectSerializer(serializers.ModelSerializer):
    subjects = SubjectSerializer(many=True, read_only=True)

    class Meta:
        model = Semester
        fields = ["id", "name", "subjects"]


class StudentSerializer(serializers.ModelSerializer):
    semester = SemesterSerializer()

    class Meta:
        model = Student
        fields = ["id", "name", "email", "semester"]

    def create(self, validated_data):
        semester = validated_data.pop("semester")
        serializer = SemesterSerializer(semester)
        student = Student.objects.create(
            id=None, semester_id=serializer.data["id"], **validated_data
        )
        return student

Я пробовал добавить валидацию и на уровне модели, но, похоже, ничего не вышло. В любом случае я получаю IntegrityError.

from django.core.validators import MinLengthValidator, MaxLengthValidator
from .helpers import SEMESTER_NOT_STORED

def validate_semester(id):
    semesters = list(Semester.objects.all().values_list("id", flat=True))
    if id not in semesters:
        raise ValueError({"id":[SEMESTER_NOT_STORED.format(id)]})

class Semester(models.Model):
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name

class Student(models.Model):
    name = models.CharField(max_length=30)
    email = models.EmailField()
    semester = models.ForeignKey(Semester, on_delete=models.DO_NOTHING, default=None, validators=[validate_semester])

Создайте общий сериализатор с помощью сериализатора PrimaryKeyRelatedField. Он будет разрешать только существующий экземпляр семестра. Нет необходимости проверять вручную. Наследуйте общий класс сериализатора в обоих классах сериализаторов семестра и студента. Не нужно писать методы validate & create. Обратитесь к документации по сериализаторам фреймворка rest для получения дополнительных знаний о сериализаторах Fields. нажмите здесь для получения документации

from rest_framework import serializers
from .models import *


class CommonSemesterValidateSerializer(serializerz.ModelSerializer):
    semester = serializers.PrimaryKeyRelatedField(queryset=Semester.objects.all(), required=True)

    class Meta:
        fields = ["id", "name", "semester"]

class SubjectSerializer(CommonSemesterValidateSerializer):

    class Meta(CommonSemesterValidateSerializer.Meta):
        model = Subject
        fields = CommonSemesterValidateSerializer.Meta.fields + ["code"]

class StudentSerializer(CommonSemesterValidateSerializer):

    class Meta(CommonSemesterValidateSerializer.Meta):
        model = Student
        fields = CommonSemesterValidateSerializer.Meta.fields + ["email"]

Дополнительно к сказанному выше ответу, я бы не рекомендовал вам наследовать класс Meta и писать его вручную, при таком подходе у вас могут возникнуть проблемы с совместимостью с сериализатором, и он будет менее читабельным для других разработчиков. Более того, если вы хотите сериализовать модель семестра в ответе, вы можете поступить следующим образом:

semester = serializers.SemesterSerializer()
semester_id = serializers.PrimaryKeyRelatedField(
                    queryset=Semester.objects.all(), write_only=True, 
                    required=True, source='semester'
              )

Я нашел более простое решение, которое легко справляется со своей задачей. Дайте мне знать, если оно может стать лучше или есть какой-либо кэш с ним.

class SemesterSerializer(serializers.Serializer):
    id = serializers.ChoiceField(choices=Semester.objects.all().values_list('id', flat=True), required=False)
    name = serializers.CharField(max_length=100, required=False)

Здесь мы используем поле выбора, чтобы ограничить потребителей в использовании любых данных, кроме сохраненных.

Вернуться на верх