Rest框架:用于post/put操作的输入和输出数据的不同序列化器

Kil*_*elo 5 python django rest web-services django-rest-framework

假设我有这些模型:

class Download(MPTTTimeStampedModel):
    endpoint = models.ForeignKey(EndPoint, related_name="downloads",)

class EndPoint(TimeStampedModel):
    name = models.CharField(max_length=100, verbose_name=_(u"Nombre"))
    url = models.CharField(max_length=2000, verbose_name=_(u"Url"))
Run Code Online (Sandbox Code Playgroud)

这些序列化器:

class DownloadSerializer(serializers.ModelSerializer):

    class Meta:
        model = Download
        fields = ('id', 'endpoint')  

    def create(self, validated_data):
        ...

    def update(self, validated_data):
        ...


class EndPointSerializer(serializers.ModelSerializer):

    class Meta:
        model = EndPoint
        fields = ('id', 'name', 'url')

    def create(self, validated_data):
        ...

    def update(self, validated_data):
        ...
Run Code Online (Sandbox Code Playgroud)

这个通用 api 视图:

class DownloadList(generics.ListCreateAPIView):
    queryset = Download.objects.all()
    serializer_class = DownloadSerializer
Run Code Online (Sandbox Code Playgroud)

这将允许我通过发送如下所示的 json 表示来创建下载:

{
    'id': null,
    'endpoint': 19
}
Run Code Online (Sandbox Code Playgroud)

创建后,Web 服务将使用数据库中的 ID 向我发送回数据。现在,我实际上希望 Web 服务不仅向我发回端点 ID,还向我发回对象的完整表示形式,如下所示:

{
    'id': null,
    'endpoint': {
        'id': 19,
        'name': 'my endpoint',
        'url': 'http://www.my-endpoint.com/'
    }
}
Run Code Online (Sandbox Code Playgroud)

我会用这个序列化器来管理这个:

class DownloadDetailedSerializer(DownloadSerializer): 
    endpoint = EndPointSerializer(many = False, read_only=False)
Run Code Online (Sandbox Code Playgroud)

现在的实际问题是:我如何告诉我的通用视图使用最后一个序列化器来返回数据,同时保留原始的 DownloadSerializer 作为输入?

编辑:正如 @neverwalkeralone 建议的方法是创建一个自定义字段并覆盖 to_representation 方法。但这导致我在序列化器 = EndPointSerializer(value)行中出现异常,经过一些测试后我发现最好从RelatedField继承我的自定义字段。这也意味着重写 to_internal_value 。所以这就是最终完成工作的内容:

class EndPointField(serializers.RelatedField):

    def to_representation(self, value):
        serializer = EndPointSerializer(value)
        return serializer.data

    def to_internal_value(self, instance):
        endpoint = EndPoint.objects.get(pk=instance)
        return endpoint

    def get_queryset(self):
        return EndPoint.objects.all()
Run Code Online (Sandbox Code Playgroud)

nev*_*ner 5

您可以定义自定义字段,使用to_representation方法自定义输出格式:

class EndPointField(serializers.PrimaryKeyRelatedField):

    def to_representation(self, value):
        serializer = EndPointSerializer(value)
        return serializer.data

    def get_queryset(self):
        return models.EndPoint.objects.all()
Run Code Online (Sandbox Code Playgroud)

并将其用于DownloadSerializer端点字段:

class DownloadSerializer(serializers.ModelSerializer):
    endpoint = EndPointField()

    class Meta:
        model = Download
        fields = ('id', 'endpoint')  
Run Code Online (Sandbox Code Playgroud)

UPD

根据 Kilian Perdomo Curbelo 反馈EndPointFieldto_representation值应替换为端点实例:

def to_representation(self, value):
    endpoint = EndPoint.objects.get(pk=value.pk)
    serializer = EndPointSerializer(endpoint)
    return serializer.data
Run Code Online (Sandbox Code Playgroud)