Django POST请求报错'GenericRelatedObjectManager' object has no attribute 'lang'
问题根因
- POST返回序列化报错:POST请求完成后返回响应时,你为泛关系字段绑定的是
TranslationSerializer(many=False),但Tags模型的name/description是GenericRelation类型,本质是GenericRelatedObjectManager关系管理器,不是单个Translation实例,触发to_representation时访问value.lang自然报错。刷新后走GET请求,绑定的是SerializerTranslationField所以正常。 - many=True入参报错:
many=True模式下DRF默认要求入参是数组,但你传入的是多语言键值对对象,格式不匹配触发校验错误。 - 代码存在几处低级错误:
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
相关产品推荐
相关产品推荐

