嵌套的序列化程序,可选字段引发KeyError

时间:2017-01-02 13:49:57

标签: django django-rest-framework

我有两个序列化程序,第一个(稍微简化):

class FilterSerializer(serializers.Serializer):
    brand = serializers.PrimaryKeyRelatedField(
        queryset=org_models.Brand.objects, many=False,
        error_messages={'does_not_exist': org_consts.BRAND_NOT_EXIST}
    )

    country = serializers.PrimaryKeyRelatedField(
        queryset=org_models.Country.objects, many=False,
        error_messages={'does_not_exist': org_consts.COUNTRY_NOT_EXIST}
    )

    level = serializers.PrimaryKeyRelatedField(
        queryset=org_models.Level.objects, many=False, required=False,
        error_messages={'does_not_exist': org_consts.DISTRICT_NOT_EXIST}
    )

    class Meta:
        fields = ('brand', 'country', 'level')

和第二:

class PeopleReportSerializer(serializers.Serializer):
    filters = FilterSerializer(many=True)
    start_date = serializers.DateField(required=False)
    end_date = serializers.DateField(required=False)

    class Meta:
        fields = ('filters', 'start_date', 'end_date')

    def validate(self, data):
        """Check if end_date occurs after start_date.
        """
        if data.get('start_date') and data.get('end_date'):
            if data['start_date'] > data['end_date']:
                raise serializers.ValidationError(constants.INVALID_START_DATE)
        return data

为清楚起见,我删除了一些字段。 因此,如果数据库中存在具有已发布ID的对象,那么基本上我想从此序列化程序进行验证。在GET上我希望它根据request.user返回一些数据,但这是另一种情况。

所以现在在我看来我正在做这样的事情:

class PeopleReportView(ReportsPermissionMixin, views.APIView):
    serializer_class = PeopleReportSerializer
    task = staticmethod(people_report)  # celery task, creates reports

    def get(self, request):
        # TODO: depending on request.user return initial data such as:
        # country / brand / district and so on
        return Response()

    def post(self, request):
        serializer = self.serializer_class(data=request.data)
        if serializer.is_valid(raise_exception=True):
            task = self.task.apply_async(kwargs=serializer.data)
            return Response({'task_id': task.id})

我的问题是,当我发布没有level字段的数据(这不是必填字段)时,我得到KeyError: 'level' FilterSerializer工作正常,但是当它嵌套时我得到了这个错误。 显然我错了,我的测试没有测试data属性,只有is_valid方法。

2 个答案:

答案 0 :(得分:0)

请尝试使用allow_null标志:

 level = serializers.PrimaryKeyRelatedField(
    queryset=org_models.Level.objects, many=False, allow_null=True,
    error_messages={'does_not_exist': org_consts.DISTRICT_NOT_EXIST}
)

而不是:

 level = serializers.PrimaryKeyRelatedField(
    queryset=org_models.Level.objects, many=False, required=False,
    error_messages={'does_not_exist': org_consts.DISTRICT_NOT_EXIST}
)

有关详细信息,请参阅here

答案 1 :(得分:0)

即使PrimaryKeyRelatedFieldField,但在使用required=False进行初始化时,它的工作方式也不同。就像Remi写的那样,当前版本的DRF(3.5.X)支持读/写字段的唯一方法是allow_null这些字段(除了使用一些奇怪的解决方法)。

API必须发送一个空字段,并且在对象表示 Serializer.data中,这些字段将具有None值。因为在我的情况下,我不能没有我修改Serialzier.to_representation方法的值,如下所示:

def to_representation(self, instance):
        """Excludes fields with None value from representation
        """
        ret = super().to_representation(instance)
        return {field: value for (field, value) in ret.items()
                if value is not None}