Django1.9升级至2.0后自定义MultilingualCharField报错求助
解决Django 2.0中自定义MultilingualCharField的AttributeError问题
首先咱们来拆解问题的核心:
在Django 2.0中,rel属性已经被移除(外键类字段改用remote_field),但你的MultilingualCharField继承自CharField,而CharField从一开始就不需要rel或remote_field参数——原来的代码里错误地把这个无关参数传给了动态生成的CharField实例,这在Django 1.9中可能因为参数校验宽松没报错,但2.0的字段构造函数变得严格,直接抛出了错误。
具体修复步骤
1. 移除多余的rel参数
在contribute_to_class方法里,创建localized_field时直接删掉rel=self.rel这一行,因为CharField不接受这个参数,而且你的自定义字段本身也没有这个属性。
2. 修正translated_value方法的变量错误
原代码中translated_value里的name变量未定义,应该用self.name来获取字段名;同时改用getattr替代直接访问__dict__,避免字段不存在时的KeyError。
3. 修复代码缩进错误
原代码里setattr(cls, name, property(translated_value))的缩进位置错误,它应该属于contribute_to_class方法内部,否则会在类定义阶段就执行,导致逻辑错误。
4. 替换过时的string_concat(可选)
如果string_concat是Django 1.9的过时方法,可以直接用字符串格式化替代,更直观简洁。
修正后的完整代码
from django.db import models from django.utils.translation import get_language from django.conf import settings class MultilingualCharField(models.CharField): def __init__(self, verbose_name=None, **kwargs): self._blank = kwargs.get("blank", False) self._editable = kwargs.get("editable", True) super().__init__(verbose_name, **kwargs) def contribute_to_class(self, cls, name, virtual_only=False): # 动态生成特定语言字段 if not cls._meta.abstract: for lang_code, lang_name in settings.LANGUAGES: _blank = self._blank if lang_code == settings.LANGUAGE_CODE else True # 用字符串格式化替代string_concat verbose_name_localized = f"{self.verbose_name} ({lang_code})" localized_field = models.CharField( verbose_name_localized, max_length=self.max_length, unique=self.unique, blank=_blank, null=False, # 保持原逻辑:忽略null参数 db_index=self.db_index, default=self.default or "", editable=self._editable, serialize=self.serialize, choices=self.choices, help_text=self.help_text, db_tablespace=self.db_tablespace ) localized_field.contribute_to_class(cls, f"{name}_{lang_code}") # 将属性绑定到类,修正缩进位置 def translated_value(self_instance): language = get_language() current_lang_val = getattr(self_instance, f"{self.name}_{language}", "") if not current_lang_val: current_lang_val = getattr(self_instance, f"{self.name}_{settings.LANGUAGE_CODE}", "") return current_lang_val setattr(cls, name, property(translated_value)) # 修正原name方法的逻辑问题 @property def name(self): # 返回字段的基础名称,若需带默认语言后缀可保留原逻辑 return self._name
额外说明
- 为什么替换
rel为remote_field会报错?因为CharField的构造函数根本不接受remote_field参数——这个参数是为外键、一对一这类关联字段设计的,普通字符字段完全不需要。 - 修正后的代码在Django 2.0+中可以正常运行,同时完整保留了原有的多语言字段动态生成逻辑。
- 使用
getattr替代直接访问__dict__,让代码更健壮,避免字段未初始化时的异常。
内容的提问来源于stack exchange,提问作者fabio
相关产品推荐
相关产品推荐

