我的Book
模型可以通过m2m关系将Tag
模型与其关联,这些标签总是通过FKTagCategory
模型进一步分类。
class TagCategory(models.Model):
name = models.CharField(...)
class Tag(models.Model):
name = models.CharField(...)
category = models.ForeignKey(to=TagCategory, ...)
class Book(models.Model):
tags = models.ManyToManyField(to=Tag, ...)
使用DRF中的序列化程序,我希望BookSerializer
提供一个MultipleChoiceField
,供最终用户从中选择许多标签。如果我使用serializers.ModelSerializer
并且不接触任何东西,它可以正常工作。
但我实际上想按类别对标签/选项进行分组,这不是默认行为,所以我必须定义自己的MultipleChoiceField
,在choices
中对选项进行分组并提醒字段从Book
模型的正确属性中获取其source
:
class BookSerializer(serializers.ModelSerializer):
tags_input = serializers.MultipleChoiceField(
choices=[
[category.name, [[tag.pk, tag.name]
for tag in category.tags.all()]] for category in Category.objects.all()],
source="tags",
write_only=True,
)
fields = ("tags_input",)
这似乎适用于创建Book
模型和通过m2m分配标签。但是,当用标记序列化现有模型以进行更新时,初始标记/选项并没有按预期预先选择。设置initial="tags"
似乎不会改变任何内容。
在序列化模型时,如何从Book.tags
中成功检索和预选标记/选项?
好的,所以我在这里的困惑是认为ModelSerializer
实际上会使用MultipleChoiceField
,所以我可以击败它并自己定义它:不。tags
是m2m关系,所以我需要一个RelatedField
和many=True
。
首先,由于我是从同一个模型字段序列化和反序列化的,所以我不应该使用我提出的tags_input
解决方法。我需要一个能做所有事情的领域:
class TagChoiceRelatedField(serializers.RelatedField):
queryset = Tags.objects.all()
def to_representation(self, value):
return value.pk
def get_choices(self, cutoff=None):
return {
category.name:
{tag.pk: tag.name for tag in category.tags.all()}
for category in TagCategory.objects.all()
}
def to_internal_value(self, data):
try:
tag = self.get_queryset().get(pk=data)
except ObjectDoesNotExist:
raise ValidationError()
return tag
class BookSerializer(serializers.ModelSerializer):
tags = TagChoiceRelatedField(many=True)
class Meta:
fields = ("tags",)
串行器现在使用该字段:
- 期望将每个标签及其pk序列化(to_representation(
- 预期使用pk(to_internal_value(反序列化每个标签
- 很好地根据标签/选项的类别对其进行分组(get_choices(
- 如果适用的话,最后用book实例的标记初始化后者(内部的东西(