You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将Django序列化器数据转google.protobuf.Struct时遇TypeError错误

问题描述

需要创建如下gRPC响应消息:

message Match {
  int32 index = 1;
  google.protobuf.Struct match = 2;
  google.protobuf.Struct status = 3;
}

使用Django嵌套序列化器将数据转换为protobuf Struct时触发类型错误,序列化器代码如下:

class MatchSerializer(proto_serializers.ModelProtoSerializer):
    competition = serializers.SerializerMethodField()
    match = serializers.SerializerMethodField()
    status = serializers.SerializerMethodField()

    class Meta:
        model = Match
        proto_class = base_pb2.Match
        fields = ['index', 'match', 'competition', 'status']

    def get_competition(self, obj):
        data = SomeSerializer(obj.competition).data
        struct_data = struct_pb2.Struct()
        return struct_data.update(data)

    def get_status(self, obj):
        status_dict = {
            0: {0: "not started"},
            1: {1: "finished"},
            2: {9: "live"},
        }
        result = status_dict[obj.status]
        struct_data = struct_pb2.Struct()
        return struct_data.update(result)
    
    def get_match(self, obj):
        data = SomeOtherSerializer(obj.match).data
        struct_data = struct_pb2.Struct()
        return struct_data.update(data)

错误栈信息:

Traceback (most recent call last):
  File "/project/venv/lib/python3.10/site-packages/grpc/_server.py", line 555, in _call_behavior
    response_or_iterator = behavior(argument, context)
  File "/project/base/grpc/services.py", line 44, in GetUserSubscribedTeamsMatches
    return MatchSerializer(val).message
  File "/project/venv/lib/python3.10/site-packages/django_grpc_framework/proto_serializers.py", line 31, in message
    self._message = self.data_to_message(self.data)
  File "/project/venv/lib/python3.10/site-packages/rest_framework/serializers.py", line 555, in data
    ret = super().data
  File "/project/venv/lib/python3.10/site-packages/rest_framework/serializers.py", line 253, in data
    self._data = self.to_representation(self.instance)
  File "/project/venv/lib/python3.10/site-packages/rest_framework/serializers.py", line 522, in to_representation
    ret[field.field_name] = field.to_representation(attribute)
  File "/project/venv/lib/python3.10/site-packages/rest_framework/fields.py", line 1838, in to_representation
    return method(value)
  File "/project/base/grpc/serializers.py", line 97, in get_matches
    return struct_data.update(data[0])
  File "/project/venv/lib/python3.10/site-packages/google/protobuf/internal/well_known_types.py", line 522, in update
    _SetStructValue(self.fields[key], value)
  File "/project/venv/lib/python3.10/site-packages/google/protobuf/internal/well_known_types.py", line 448, in _SetStructValue
    struct_value.struct_value.update(value)
  File "/project/venv/lib/python3.10/site-packages/google/protobuf/internal/well_known_types.py", line 522, in update
    _SetStructValue(self.fields[key], value)
TypeError: bad argument type for built-in operation
解决方法

1. 修正update()方法的返回逻辑

struct_pb2.Struct().update()是原地修改结构体的方法,返回值为None,但序列化器需要返回Struct对象本身。正确写法是先执行更新,再返回结构体:

def get_competition(self, obj):
    data = SomeSerializer(obj.competition).data
    struct_data = struct_pb2.Struct()
    struct_data.update(data)
    return struct_data

2. 确保字典键为字符串类型

Protobuf的Struct要求所有键必须是字符串,你在status_dict中使用了整数作为内层键,会触发类型错误。需要将键转换为字符串:

def get_status(self, obj):
    status_dict = {
        0: {"0": "not started"},
        1: {"1": "finished"},
        2: {"9": "live"},
    }
    result = status_dict[obj.status]
    struct_data = struct_pb2.Struct()
    struct_data.update(result)
    return struct_data

3. 转换序列化器数据为兼容类型

Django序列化器返回的data可能包含Protobuf Struct不支持的类型(如Decimal、datetime对象),需要提前转换:

from decimal import Decimal
from datetime import datetime

def convert_to_struct_compatible(data):
    if isinstance(data, dict):
        return {str(k): convert_to_struct_compatible(v) for k, v in data.items()}
    elif isinstance(data, list):
        return [convert_to_struct_compatible(item) for item in data]
    elif isinstance(data, Decimal):
        return float(data)  # 或转为字符串,根据业务需求选择
    elif isinstance(data, datetime):
        return data.isoformat()
    else:
        return data

在序列化器中使用该转换函数:

def get_match(self, obj):
    data = SomeOtherSerializer(obj.match).data
    compatible_data = convert_to_struct_compatible(data)
    struct_data = struct_pb2.Struct()
    struct_data.update(compatible_data)
    return struct_data

4. 排查get_matches方法的异常

错误栈中提到get_matches方法调用struct_data.update(data[0]),但你提供的代码中没有这个方法。需要确认data[0]是否为有效的字典类型,避免传递非字典数据给update()方法。

内容的提问来源于stack exchange,提问作者Mahsa Fathi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 12:50:55