Я работаю над проектом, который управляет персоналом школы. На мой взгляд, отношения между ними плохо реализованы, поэтому я пытаюсь их реорганизовать. Тем не менее, чтобы минимизировать влияние, поскольку это не является приоритетом, я пытаюсь сделать это, не нарушая интерфейс, который должен быть реорганизован позднее. Текущая модель Staff
выглядит следующим образом:
class Staff(models.Model):
principal_at = models.ForeignKey(School, null=True)
teacher_at = models.ForeignKey(School, null=True)
# I'm only showing these two roles for the sake of simplicity but there are more.
Я рефакторинг ее следующим образом:
class Staff(models.Model):
ROLE_CHOICES = (
"principal",
"teacher",
# ...
)
school = models.ForeignKey(School)
role = models.CharField(choices=ROLE_CHOICES)
# ...
@property
def principal_at(self):
return self.school if self.role == "principal" else None
# ...
Мой StaffSerializer
выглядит следующим образом:
class StaffSerializer(serializers.ModelSerializer):
class Meta:
model = Staff
fields = (
"principal_at",
"teacher_at",
# ...
)
# There is also some custom validation to ensure the Staff can only have
# one role.
Если я просто оставлю его без изменений, поле principal_at
станет ReadOnlyField
, и оно не будет сохранено, что имеет смысл, поскольку мой атрибут модели теперь является свойством, а не фактическим записываемым полем. Поэтому я изменил свой сериализатор следующим образом:
class StaffSerializer(serializers.ModelSerializer):
kwargs = {"required": False, "queryset": School.objects.all()}
principal_at = serializers.PrimaryKeyRelatedField(**kwargs)
teacher_at = serializers.PrimaryKeyRelatedField(**kwargs)
class Meta:
model = Staff
fields = (
"principal_at",
"teacher_at",
# ...
)
def validate(self, data):
principal_at = data.pop("principal_at", None)
teacher_at = data.pop("teacher_at", None)
# ...
if principal_at:
data.update(school_id=principal_at, role="principal")
elif teacher_at:
data.update(school_id=teacher_at, role="teacher")
# ...
return data
Теперь модель сохранена правильно (я проверил с помощью ipdb), но в процессе построения ответа я получаю следующее:
TypeError: Object of type School is not JSON serializable
После этого я, честно говоря, не помню, что я пробовал, но я пробовал много вещей, и ничего не работает. Одна вещь, которую я помню, пытаясь, и это поразило меня, было создание настраиваемого поля и переопределение to_representation
:
class RelatedSchoolSerializer(serializers.PrimaryKeyRelatedField):
def to_representation(self, value):
import ipdb; ipdb.set_trace()
return value.pk
class StaffSerializer(serializers.ModelSerializer):
kwargs = {"required": False, "queryset": School.objects.all()}
principal_at = serializers.PrimaryKeyRelatedField(**kwargs)
teacher_at = serializers.PrimaryKeyRelatedField(**kwargs)
# ...
Это продолжало давать сбой с той же ошибкой, однако, побудив меня добавить точку останова и проверить, что F ** K продолжается. В отладчике, к моему ужасу, я получил следующее:
ipdb> value
<rest_framework.relations.PKOnlyObject object at 0x7f9563c87f90>
ipdb> value.pk
<School: School 0>
ipdb> value.pk.pk
1
ipdb>
Если я верну value.pk.pk
вместо value.pk
, все работает. Но хотя мой набор тестов проходит, я боюсь, что такой странный взлом может иметь загадочные непредвиденные последствия в будущем. Что на земле происходит? Как правильно это сделать?