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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 06:52:26