Django Rest Framework 不序列化 SerializerMethodField

Posted

技术标签:

【中文标题】Django Rest Framework 不序列化 SerializerMethodField【英文标题】:Django Rest Framework doesn't serialize SerializerMethodField 【发布时间】:2016-07-21 18:59:30 【问题描述】:

我有 2 个模型:

from django.db import models

STATUSES = (
    ('f', 'Finished'),
)


class Battery(models.Model):
    energy = models.CharField(max_length=10)
    current = models.CharField(max_length=10)


class Charger(models.Model):
    status = models.CharField(max_length=1, choices=STATUSES)

我想创建将这 2 个模型一起序列化的序列化程序。我的 serializers.py:

from rest_framework import serializers
from .models import Battery, Charger


class BatterySerializer(serializers.ModelSerializer):
    class Meta:
        model = Battery


class ChargerSerializer(serializers.ModelSerializer):
    status = serializers.SerializerMethodField()

    class Meta:
        model = Charger

    def get_status(self, obj):
        return obj.get_status_display()


class DeviceSerializer(serializers.Serializer):
    battery = BatterySerializer()
    charger = ChargerSerializer()
    some_field = serializers.CharField()

因为 Charger 模型在状态字段中有选择,所以我添加了 SerializerMethodField 来显示完整状态。然后我创建一个这样的视图:

class DeviceView(APIView):
    def get(self, request, format=None):
        battery = Battery.objects.get(id=1)
        charger = Charger.objects.get(id=1)
        battery_serializer = BatterySerializer(battery)
        charger_serializer = ChargerSerializer(charger)
        serializer = DeviceSerializer(data=
            'battery': battery_serializer.data,
            'charger': charger_serializer.data,
            'some_field': 'some_text'
        )
        if serializer.is_valid():
            return Response(serializer.validated_data)
        else:
            return Response(status = 500)

但是当我调用这个视图时,它会返回带有空充电器字段的 json:


    "battery": 
        "energy": "12",
        "current": "34"
    ,
    "charger": ,
    "some_field": "some_text"

但是当我创建一个仅序列化 Charger 模型的视图时:

class ChargerView(APIView):
    def get(self, request, format=None):
        charger = Charger.objects.get(id=1)
        charger_serializer = ChargerSerializer(charger)
        return Response(charger_serializer.data)

它可以工作并返回这个 json:


    "id": 1,
    "status": "Finished"

为什么会这样?我哪里做错了?

【问题讨论】:

为什么不能在视图中同时调用BatterySerializerChargerSerializer 方法并返回它们?因为那是你的DeviceSerializer正在做的事情。 如何将它们一起退回? 喜欢这个 'battery': battery.__dict__, 'charger': charger.__dict__, 'some_field': 'some_text' ?不确定这是否是正确的方法。 您在serializer.is_valid() 中验证什么?可以直接回Response(serializer.data)看看有什么收获吗? 当我尝试返回 Response(serializer.data) 它引发 AssertionError: When a serializer is passed a data keyword argument you must call .is_valid() before attempting to access the serialized .data representation. You should either call .is_valid() first, or access .initial_data instead. 【参考方案1】:

查看Serializers的文档:

    instance 当你有一个对象并且你必须序列化它时传递。 (link) data 当你已经有序列化数据并且你想反序列化它并从中创建一个实例时传递。(link) instancedata 在您有实例并且想要更新它时都被传递。(link)

看你的情况,我认为你不需要选项 2 和 3,因为你有 batterycharger 实例,你需要一起序列化它。您没有创建新实例,也不必验证它,因此不需要将其作为 data 传递。

有两种方法可以做到这一点:

1.创建一个类Device,这样您就可以创建它的一个实例,然后使用DeviceSerializer对其进行序列化:

class Device(object):

    def __init__(self, battery, charger, some_field):
        self.battery = battery
        self.charger = charger
        self.some_field  = some_field

class DeviceView(APIView):
    # then in the DeviceView you could create an instance and pass to the serializer
    def get(self, request, format=None):
        battery = Battery.objects.get(id=1)
        charger = Charger.objects.get(id=1)
        device = Device(battery=battery, charger=charger, some_field='some_text')
        serializer = DeviceSerializer(instance=device)
        return Response(serializer.data)

2.如果您不想创建一个新类,您可以直接创建一个dict 并将其作为实例传递:

class DeviceView(APIView):
    def get(self, request, format=None):
        battery = Battery.objects.get(id=1)
        charger = Charger.objects.get(id=1)
        # create a dict with required objects and pass it as instance of serializer
        device = 'battery': battery, 'charger': charger, 'some_field': 'some_text'
        serializer = DeviceSerializer(instance=device)
        return Response(serializer.data)    

【讨论】:

【参考方案2】:

看起来你正在做你不必做的工作。如果在将充电器传递给DeviceSerializer 之前对其进行序列化,则实际上传递的是dict,而不是Charger 实例,并且dict 没有get_status_display 方法。您应该像这样直接传递BatteryCharger

class DeviceView(APIView):
    def get(self, request, format=None):
        battery = Battery.objects.get(id=1)
        charger = Charger.objects.get(id=1)
        serializer = DeviceSerializer(instance=
            'battery': battery,
            'charger': charger,
            'some_field': 'some_text',
        )
        return Response(serializer.data)

请注意,您还可以通过将 SerializerMethodField 替换为 CharField 来简化:

class ChargerSerializer(serializers.ModelSerializer):
    status = serializers.CharField(source='get_status_display')

    class Meta:
        model = Charger

编辑:正如 AKS 指出的那样,序列化时应通过instance 而不是data 序列化程序(data 用于反序列化),您无需检查.is_valid()

【讨论】:

这行不通。 Invalid data, expected a dictionary, but got Battery/Charger 已编辑以将 dict 传递为 instance 而不是 data【参考方案3】:

您在创建序列化程序实例时传递关键字 data,witch 仅在反序列化数据时使用。 您应该使用具有所需字段的对象创建 DeviceSerializer。 我没有测试过,但可能是这样的

class Device(object):
    def __init__(self, battery, charger, name, ):
        self.battery = battery
        self.charger = charger
        self.some_field = name

class DeviceView(APIView):
    def get(self, request, format=None):

        d=Device(Battery.objects.get(id=1),Charger.objects.get(id=1),"somename")
        serializer = DeviceSerializer(d)
            return Response(serializer.data)

【讨论】:

以上是关于Django Rest Framework 不序列化 SerializerMethodField的主要内容,如果未能解决你的问题,请参考以下文章

基于Django的Rest Framework框架的序列化组件

Django REST Framework 序列化程序字段必需=false

django-rest-framework:如何序列化已经包含 JSON 的字段?

Django-rest-framework --- 总结

为啥 Django Rest Framework 不鼓励模型级别验证?

Django 序列化器与 rest_framework 序列化器