django-modeltranslation启用时,动态添加ModelForm字段后翻译值无法保存的问题排查与解决问询
django-modeltranslation启用时,动态添加ModelForm字段后翻译值无法保存的问题排查与解决问询
嘿,这个坑我之前也踩过!核心原因其实很直白:Django的ModelForm只会自动处理Meta.fields里声明的字段,或者由modeltranslation自动关联生成的字段。你手动动态添加的description_es这类字段,不在ModelForm的“自动处理清单”里,所以哪怕cleaned_data里已经有正确的值,form.save()的时候也不会把这些值同步到模型实例上。
给你两个实用的解决办法,选哪个看你的代码习惯:
方法一:重写表单save方法,手动赋值动态字段
这个方法最直接,我们自己接管动态字段的值赋值工作。首先在你的DefaultModelForm里新增save方法,同时在__init__里记录下所有动态添加的字段名:
class DefaultModelForm(forms.ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # 新增实例变量,记录所有动态添加的翻译字段名 self.added_translation_fields = [] if user := self.request and self.request.user: company = user.company app_setting = company.settings default_lang = settings.MODELTRANSLATION_DEFAULT_LANGUAGE # "en" default_search = f"_{default_lang}" to_add = [] """Set field_order to the same order as set in Meta.fields, unless already set""" if self.field_order is None: self.field_order = list(self.Meta.fields) for modelfield in self._meta.model._meta.fields: """If we found a default translation field, add it to the list""" if ( (formfield := self.fields.get(modelfield.name, None)) and modelfield.name.endswith(default_search) and isinstance(modelfield, TranslationField) ): to_add.append( { "position": self.field_order.index(modelfield.name), "formfield": deepcopy(formfield), "name": modelfield.name.removesuffix(default_search), # "description" "languages": app_setting.get_additional_language_codes, # ["es"] } ) for addable in to_add: for lang in addable.get("languages"): field_name = f"{addable.get('name')}_{lang}" # "description_es" formfield = addable.get("formfield") formfield.label = formfield.label.replace(f"[{default_lang}]", f"[{lang}]") formfield.required = False formfield.initial = getattr(self.instance, field_name, "") self.fields[field_name] = formfield # 记录添加的字段名 self.added_translation_fields.append(field_name) self.field_order.insert(addable.get("position", 0) + 1, field_name) self.order_fields(self.field_order) def save(self, commit=True): # 先调用父类save方法,暂不提交到数据库 instance = super().save(commit=False) # 遍历动态字段,把cleaned_data的值赋值给实例 for field_name in self.added_translation_fields: if field_name in self.cleaned_data: setattr(instance, field_name, self.cleaned_data[field_name]) # 提交保存(如果需要) if commit: instance.save() return instance
方法二:动态更新Meta.fields,让ModelForm自动处理
如果你想让ModelForm自己处理这些字段,可以在__init__里把动态添加的字段加入到self._meta.fields中,这样Django会自动同步字段值到实例:
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # 存储要添加的额外字段 additional_fields = [] if user := self.request and self.request.user: company = user.company app_setting = company.settings default_lang = settings.MODELTRANSLATION_DEFAULT_LANGUAGE # "en" default_search = f"_{default_lang}" to_add = [] """Set field_order to the same order as set in Meta.fields, unless already set""" if self.field_order is None: self.field_order = list(self.Meta.fields) for modelfield in self._meta.model._meta.fields: """If we found a default translation field, add it to the list""" if ( (formfield := self.fields.get(modelfield.name, None)) and modelfield.name.endswith(default_search) and isinstance(modelfield, TranslationField) ): to_add.append( { "position": self.field_order.index(modelfield.name), "formfield": deepcopy(formfield), "name": modelfield.name.removesuffix(default_search), # "description" "languages": app_setting.get_additional_language_codes, # ["es"] } ) for addable in to_add: for lang in addable.get("languages"): field_name = f"{addable.get('name')}_{lang}" # "description_es" formfield = addable.get("formfield") formfield.label = formfield.label.replace(f"[{default_lang}]", f"[{lang}]") formfield.required = False formfield.initial = getattr(self.instance, field_name, "") self.fields[field_name] = formfield additional_fields.append(field_name) self.field_order.insert(addable.get("position", 0) + 1, field_name) # 更新Meta.fields,加入额外字段(转成列表避免元组不可修改) self._meta.fields = list(self._meta.fields) + additional_fields self.order_fields(self.field_order)
这两种方法都能解决你的问题,修改后再调试,应该就能看到self.object.description_es拿到正确的POST值啦!
备注:内容来源于stack exchange,提问作者kunambi
相关产品推荐
相关产品推荐

