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

Django POST请求报错'GenericRelatedObjectManager' object has no attribute 'lang'

问题根因

  1. POST返回序列化报错:POST请求完成后返回响应时,你为泛关系字段绑定的是TranslationSerializer(many=False),但Tags模型的name/description是GenericRelation类型,本质是GenericRelatedObjectManager关系管理器,不是单个Translation实例,触发to_representation时访问value.lang自然报错。刷新后走GET请求,绑定的是SerializerTranslationField所以正常。
  2. many=True入参报错:many=True模式下DRF默认要求入参是数组,但你传入的是多语言键值对对象,格式不匹配触发校验错误。
  3. 代码存在几处低级错误:
    • get_generic_relation_fields是实例方法,调用时应该用self.get_generic_relation_fields(),原代码直接写了函数名调用
    • create方法中调用translation_create_field是实例方法,应该用self.translation_create_field()
    • SerializerTranslationField.to_representation中过滤条件object_id=value错误,value是Tags实例,应该传object_id=value.id

修复方案

1. 调整序列化器动态绑定逻辑

不要按请求方法切换序列化输出字段,统一用SerializerTranslationField做输出,仅为写请求添加专属的写入字段(设置write_only=True避免参与序列化):

def __init__(self, *args, **kwargs):
    super(self.__class__, self).__init__(*args, **kwargs)
    # 先获取所有泛关系字段
    generic_fields = self.get_generic_relation_fields()
    # 所有场景统一用SerializerTranslationField做输出
    for field in generic_fields:
        self.fields[field] = SerializerTranslationField(
            translation_field=field, model_name=self.Meta.model
        )
    # 写请求额外加写入字段
    try:
        if self.context['request'].method in ['POST', 'PUT', 'PATCH']:
            for field in generic_fields:
                # 直接用DictField接收多语言字典,没必要套TranslationSerializer
                self.fields[f"{field}_input"] = serializers.DictField(
                    child=serializers.CharField(), required=False, write_only=True
                )
    except KeyError:
        pass

2. 调整create方法的取值逻辑

从新增的xxx_input字段里取值做翻译创建:

def create(self, validated_data):
    generic_fields = self.get_generic_relation_fields()
    # 提取输入的翻译数据
    translation_data = {}
    for field in generic_fields:
        input_key = f"{field}_input"
        if input_key in validated_data:
            translation_data[field] = validated_data.pop(input_key)
    # 创建Tag实例
    instance = self.Meta.model.objects.create(**validated_data)
    # 批量创建翻译
    content_type = ContentType.objects.get_for_model(self.Meta.model)
    for field, lang_map in translation_data.items():
        for lang, text in lang_map.items():
            Translation.objects.create(
                content_type=content_type,
                object_id=str(instance.id),
                field=field,
                lang=lang,
                translation=text
            )
    return instance

3. 修复SerializerTranslationField的过滤条件

def to_representation(self, value):
    content_type = ContentType.objects.get_for_model(self.model_name)
    # 修复object_id取值
    queryset = Translation.objects.filter(
        content_type=content_type, 
        object_id=str(value.id), 
        field=self.translation_field
    )
    result = TranslationSerializer(queryset, required=False, many=True).data
    final_result = {k: v for d in result for k, v in d.items()}
    return final_result

4. 调整POST请求的payload格式

把原来的name/description改成name_input/description_input即可:

{
    "type": 0,
    "name_input": {
        "en": "hello",
        "es": "ola",
        "pt": "oi"
    },
    "description_input":{
        "pt_br": "Bom dia",
        "en": "Good morning",
        "es": "Buenos dias"
    }
}

如果你不想改payload的key,也可以直接覆盖原字段,把原字段设为write_only,然后额外添加一个同名字段的read_only属性为True的SerializerTranslationField即可。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 10:06:00