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)
Здесь мы используем поле выбора, чтобы ограничить потребителей в использовании любых данных, кроме сохраненных.