如何将SlugRelatedField用于传入请求(反序列化),并将完整的序列化程序用于相关字段的响应

时间:2018-12-03 19:32:10

标签: django django-rest-framework

我的数据库模型包含事件,每个事件都连接到一个场所。 当我检索事件列表时,我使用:

venue = VenueSerializer(read_only=True)

当我发布到drf端点时,我使用:

venue = serializers.SlugRelatedField(
        allow_null=True,
        queryset=Venue.objects.all(),
        required=False,
        slug_field='id')

但是,这导致在我从发帖请求中收到的响应中,场地被序列化为一个子字段。我希望它使用VenueSerialiser进行响应。

我遇到过https://stackoverflow.com/a/49035208/5683904,但它仅适用于Viewset本身。

   #serializer_class = EventSerializer
   read_serializer_class = EventSerializer
   create_serializer_class = EventCreateUpdateSerializer

由于与其他组件共享,我需要将此功能内置到序列化器本身中。

1 个答案:

答案 0 :(得分:1)

问题

对SlugRelatedField的to_representation方法进行了编码,以返回初始化期间传递给它的slug_field关键字参数的值。

解决方法

  • 扩展SlugRelatedField并覆盖其to_representation方法以返回完整的对象而不是子对象。这可能有些棘手,因为实际的模型实例不是该类的一部分。

  • 具有两个字段,一个字段用于块,另一个字段用于实际的对象。这样更容易实现。

这是实现第二种解决方法的方法:

venue = VenueSerializer(read_only=True)
venue_id = serializers.SlugRelatedField(
        write_only=True
        allow_null=True,
        queryset=Venue.objects.all(),
        required=False,
        slug_field='id')

更新:这显然是DRF中非常想要的功能。我也找到了一种实现第一个解决方法的方法。它处理PrimaryKeyRelatedField,但您可能也可以对其进行修改以与SlugRelatedField一起使用。在这里:

from collections import OrderedDict

from rest_framework import serializers


class AsymetricRelatedField(serializers.PrimaryKeyRelatedField):

    def to_representation(self, value):
        return self.serializer_class(value).data

    def get_queryset(self):
        if self.queryset:
            return self.queryset
        return self.serializer_class.Meta.model.objects.all()

    def get_choices(self, cutoff=None):
        queryset = self.get_queryset()
        if queryset is None:
            return {}

        if cutoff is not None:
            queryset = queryset[:cutoff]

        return OrderedDict([
            (
                item.pk,
                self.display_value(item)
            )
            for item in queryset
        ])

    def use_pk_only_optimization(self):
        return False

    @classmethod
    def from_serializer(cls, serializer, name=None, args=(), kwargs={}):
        if name is None:
            name = f"{serializer.__class__.name}AsymetricAutoField"

        return type(name, [cls], {"serializer_class": serializer})

然后您可以像下面这样使用此字段:

class FooSerializer(serilizers.ModelSerializer):

    bar = AsymetricRelatedField(BarSerializer)

    class Meta:
        model = Foo

您可以找到有关此here

的原始讨论