使用 JSON 的 Django 多语言文本字段

2023-12-26

我最近问这个问题自定义 Django MultilingualTextField 模型字段 https://stackoverflow.com/questions/16750509/custom-django-multilingualtextfield-model-field但我发现没有充分的理由为什么我不应该这样做,所以我创建了一个支持多语言文本的模型字段,自动返回当前语言的文本。这基本上是以 json 格式将自定义语言对象存储到数据库的字段。这是代码:

Github: https://github.com/james4388/django-multilingualfield https://github.com/james4388/django-multilingualfield

Ussage:

from django.db import models
from multilingualfield import MLTextField, MLHTMLField

class MyModel(models.Model):
    text = MLTextField()
    html = MLHTMLField()

像普通文本字段一样使用它,翻译是根据系统语言自动进行的(translation.get_language)

>>>from django.utils import translation
>>>translation.active('en')

>>>m = MyModal.objects.create(text='Hello world',html='<b>Hello world</b>');
>>>m.text
Hello world
>>>translation.active('fr')
>>>m.text       #Auto fallback to first language (if any).
Hello world
>>>m.text.value('Bonjour')
>>>m.text.value('Ciao','es')
>>>m.text
Bonjour
>>>m.save()
>>>m.text.get_available_language()
['en', 'fr', 'es']
>>>m.text.remove_language('en')

Field.py

from __future__ import unicode_literals

from django.core.exceptions import ValidationError
from django.conf import settings
from django.db import models, DatabaseError, transaction
from django.utils.translation import ugettext_lazy as _, get_language
from django.utils import six

try:
    import json
except ImportError:
    from django.utils import simplejson as json

def get_base_language(lang):
    if '-' in lang:
        return lang.split('-')[0]
    return lang

def get_current_language(base=True):
    l = get_language()
    if base:
        return get_base_language(l)
    return l

from .widgets import MultilingualWidget, MultilingualHTMLWidget
from .forms import MultilingualTextFormField, MultilingualHTMLFormField
from .language import LanguageText

class MultilingualTextField(six.with_metaclass(models.SubfieldBase, models.Field)):
    """
    A field that support multilingual text for your model
    """
    default_error_messages = {
        'invalid': _("'%s' is not a valid JSON string.")
    }
    description = "Multilingual text field"

    def __init__(self, *args, **kwargs):
        self.lt_max_length = kwargs.pop('max_length',-1)
        self.default_language = kwargs.get('default_language', get_current_language())
        super(MultilingualTextField, self).__init__(*args, **kwargs)


    def formfield(self, **kwargs):
        defaults = {
            'form_class': MultilingualTextFormField,
            'widget': MultilingualWidget
        }
        defaults.update(**kwargs)
        return super(MultilingualTextField, self).formfield(**defaults)

    def validate(self, value, model_instance):
        if not self.null and value is None:
            raise ValidationError(self.error_messages['null'])
        try:
            self.get_prep_value(value)
        except:
            raise ValidationError(self.error_messages['invalid'] % value)

    def get_internal_type(self):
        return 'TextField'


    def db_type(self, connection):
        return 'text'

    def to_python(self, value):
        if isinstance(value, six.string_types):
            if value == "" or value is None:
                if self.null:
                    return None
                if self.blank:
                    return ""
            try:
                valuejson = json.loads(value)
                Lang = LanguageText(max_length=self.lt_max_length,default_language=self.default_language)
                Lang.values = valuejson
                return Lang
            except ValueError:
                try:
                    Lang = LanguageText(value,language=None,max_length=self.lt_max_length,default_language=self.default_language)
                    return Lang
                except:
                    msg = self.error_messages['invalid'] % value
                    raise ValidationError(msg)
        return value

    def get_db_prep_value(self, value, connection=None, prepared=None):
        return self.get_prep_value(value)

    def get_prep_value(self, value):
        if value is None:
            if not self.null and self.blank:
                return ""
            return None
        if isinstance(value, six.string_types):
            value = LanguageText(value,language=None,max_length=self.lt_max_length,default_language=self.default_language)
        if isinstance(value, LanguageText):
            value.max_length = self.lt_max_length
            value.default_language = self.default_language
            return json.dumps(value.values)
        return None

    def get_prep_lookup(self, lookup_type, value):
        if lookup_type in ["exact", "iexact"]:
            return self.to_python(self.get_prep_value(value))
        if lookup_type == "in":
            return [self.to_python(self.get_prep_value(v)) for v in value]
        if lookup_type == "isnull":
            return value
        if lookup_type in ["contains", "icontains"]:
            if isinstance(value, (list, tuple)):
                raise TypeError("Lookup type %r not supported with argument of %s" % (
                    lookup_type, type(value).__name__
                ))
                # Need a way co combine the values with '%', but don't escape that.
                return self.get_prep_value(value)[1:-1].replace(', ', r'%')
            if isinstance(value, dict):
                return self.get_prep_value(value)[1:-1]
            return self.to_python(self.get_prep_value(value))
        raise TypeError('Lookup type %r not supported' % lookup_type)

    def value_to_string(self, obj):
        return self._get_val_from_obj(obj)

Forms.py

from django import forms
from django.utils import simplejson as json

from .widgets import MultilingualWidget, MultilingualHTMLWidget
from .language import LanguageText

class MultilingualTextFormField(forms.CharField):

    widget = MultilingualWidget

    def __init__(self, *args, **kwargs):
        kwargs['widget'] = MultilingualWidget
        super(MultilingualTextFormField, self).__init__(*args, **kwargs)

    def clean(self, value):
        """
        The default is to have a TextField, and we will decode the string
        that comes back from this. However, another use of this field is
        to store a list of values, and use these in a MultipleSelect
        widget. So, if we have an object that isn't a string, then for now
        we will assume that is where it has come from.
        """
        value = super(MultilingualTextFormField, self).clean(value)
        if not value:
            return value
        if isinstance(value, basestring):
            try:
                valuejson = json.loads(value)
                Lang = LanguageText()
                Lang.values = valuejson
                return Lang
            except ValueError:
                try:
                    Lang = LanguageText(value,language=None)
                    return Lang
                except:
                    raise forms.ValidationError(
                        'JSON decode error: %s' % (unicode(exc),)
                    )
        else:
            return value

language.py 中的语言对象

from __future__ import unicode_literals

from django.core.exceptions import ValidationError
from django.conf import settings
from django.db import models, DatabaseError, transaction
from django.utils.translation import ugettext_lazy as _, get_language

try:
    import json
except ImportError:
    from django.utils import simplejson as json

def get_base_language(lang):
    if '-' in lang:
        return lang.split('-')[0]
    return lang

def get_current_language(base=True):
    l = get_language()
    if base:
        return get_base_language(l)
    return l


class LanguageText(object):
    '''
        JSON text field blah blah blah
    '''
    values = {}
    default_language = None
    max_length = -1

    def __init__(self, value=None, language=None, default_language=None, max_length=-1):
        self.max_length = max_length
        self.default_language = default_language
        self.values = {}
        if value is not None:
            self.value(value,language)

    def __call__(self, value=None, language=None):
        self.value(value,language)
        return self

    def get_available_language(self):
        return self.values.keys()

    def get_current_language(self, base=False):
        return get_current_language(base)

    def remove_language(self, lang):
        try:
            return self.values.pop(lang)
        except:
            pass

    def has_language(self, lang):
        return self.values.has_key(lang)

    def get(self, language=None, fallback=True):
        if language is None:
            curr_lang = get_current_language(False)
        else:
            curr_lang = language
        curr_lang_base = get_current_language(True)
        if curr_lang in self.values:
            return self.values[curr_lang]
        if not fallback:
            return None
        if curr_lang_base in self.values:
            return self.values[curr_lang_base]
        if self.default_language in self.values:
            return self.values[self.default_language]
        try:
            first_lang = self.values.keys()[0]
            return self.values[first_lang]
        except:
            pass
        return None

    def value(self, value=None, language=None):
        if value is None:   #Get value
            return self.get(language)
        else: #Set value
            if language is None:
                language = get_current_language(False)
            if self.max_length != -1:
                value = value[:self.max_length]
            self.values[language] = value
            return None

    def __unicode__(self):
        return self.value()

    def __str__(self):
        return unicode(self.value()).encode('utf-8')

    def __repr__(self):
        return unicode(self.value()).encode('utf-8')

小部件.py

from django import forms
from django.utils import simplejson as json
from django.conf import settings
from .language import LanguageText
from django.template import loader, Context

class MultilingualWidget(forms.Textarea):

    def __init__(self, *args, **kwargs):
        forms.Widget.__init__(self, *args, **kwargs)

    def render(self, name, value, attrs=None):
        if value is None: #New create or edit none
            vjson = '{}'
            aLang = []
            Lang = '[]'
            Langs = json.dumps(dict(settings.LANGUAGES))
            t = loader.get_template('multilingualtextarea.html')
            c = Context({"data":value,"vjson":vjson,"lang":Lang,"langs":Langs,"langobjs":settings.LANGUAGES,"fieldname":name})
            return t.render(c)
        if isinstance(value, LanguageText):
            vjson = json.dumps(value.values)
            aLang = value.get_available_language()
            Lang = json.dumps(aLang)
            Langs = json.dumps(dict(settings.LANGUAGES))
            t = loader.get_template('multilingualtextarea.html')
            c = Context({"data":value,"vjson":vjson,"lang":Lang,"langs":Langs,"langobjs":settings.LANGUAGES,"fieldname":name})
            return t.render(c)
        return "Invalid data '%s'" % value

所以我想知道这是一个好方法吗?为什么我不应该这样做?请帮忙


代码对我来说看起来不错。

唯一可能影响性能的是频繁的 json 编码/解码...但是,除非您在资源最少的服务器上面对数千个用户,否则这不会产生重大影响。

您链接到的上一个问题包含一些评论,指出添加其他语言使用其他方式可能会更容易。但最终 - 这是个人喜好和可维护性之间的混合体。如果它符合您的项目目标,我看不出有任何理由不按照您的编码方式进行操作。

提供proof你的实现是最好的几乎是不可能的。也就是说,除非您通过在生产服务器上创建不同的、非基于 json 的实现和基准测试来证明这一点。您会发现在普通机器上差异很小。然而,只有个别数字才能提供实际证据,并可以帮助您确定它是否是"tuned" and “资源友好型”足以满足您的项目目的。我认为它会满足您的需求...但这只是我的 2 美分。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 JSON 的 Django 多语言文本字段 的相关文章

随机推荐

  • 使用 Notepad++ 通过 FTP 连接到服务器

    我正在使用 Notepad 6 5 3 版本 来编辑我的文件 我想使用 Notepad 连接到我的服务器 我使用这个方法 打开 Notepad gt 插件 gt NppFTP gt 显示 NppFTP 窗口 在 NppFTP 窗口中 Cli
  • 在 .NET 中将复杂的布尔条件从字符串转换为布尔值

    我需要将复杂的表达式从字符串解析为布尔值 它只能包含 布尔值 真 假 括号 AND OR 操作数 Eg bool Parse true false false false true false 知道如何实现这一目标吗 这是一个狡猾的评估器类
  • Elasticsearch - IndicesClient.put_settings 不起作用

    我正在尝试更新我原来的索引设置 我的初始设置如下所示 client create index movies body settings number of shards 1 number of replicas 0 analysis fil
  • iPhone“书签到主屏幕”会删除 cookie 和会话吗?

    现在我正在开发一个基于网络的应用程序 用户必须首先登录 当我通过 iPhone Safari 打开页面 登录并重新启动 Safari 时 我仍然处于登录状态 Cookie 和会话 ID 仍然设置 但是 当我使用 添加到主屏幕 添加此页面时
  • spring Net 与企业库

    我一直在网上搜索有关网络框架的信息 从现在起我可以在我的项目中使用它来 保留它 我几乎已经做出了决定 但我想要一些关于该方向的参考信息市场正在继续发展 我在谷歌中找不到任何关于哪个是最常用的 哪个最有未来等等的信息 我也一直在阅读其他框架
  • CoreAnimation CALayer 和 CATextLayer 组合

    I am just playing around with CA lately Now I am kind of stuck This is the thing I want to animate 就目前而言 我已经让圆形动画正常工作了 我
  • gradle.buildStarted 未触发

    我无法在我的 gradle 构建中触发 buildStarted 不确定我做错了什么 我有一个像这样的根项目 gradle 文件 版本 1 0 buildscript repositories maven url http repo jfr
  • 如何在极坐标中的多个条件下使用“when”、“then”和“otherwise”?

    我有一个包含三列的数据集 将检查 A 列中的字符串 如果字符串匹配foo or spam 同一行中其他两列的值L and G应该改为XX 为此我尝试了以下方法 df pl DataFrame A foo ham spam egg L A54
  • Netbeans 代码生成问题:如何编辑自动生成的代码?

    好的 由于 GUI 设计器自动生成的代码 这里出现了一个 netbeans 问题 我使用 netbeans UI 设计器设计了一个 GUI 它是为 java 1 6 编译的 完成 UI 后 我意识到我应该为 Java 1 4 而不是 1 6
  • 函数式编程:列表是否只包含唯一项?

    我有一个未排序的列表 想知道其中的所有项目是否都是唯一的 我天真的做法是 val l List 1 2 3 4 3 def isUniqueList l List Int new HashSet l size l size 这是我能想到的最
  • 每 3 个 div 包裹在一个 div 中

    是否可以使用nth child使用选择器包装 3 个 div wrapAll 我似乎无法算出正确的方程式 so div div div div div div div div div div div div div div 变成 div d
  • 在适用于 Android 的 OpenGL ES 2.0 中激活/使用 GL_TEXTURE1

    我正在尝试使用 GL TEXTURE1 纹理单元来绘制一个简单的形状 我知道如何使用标准 GL TEXTURE0 绘制它 但是当更改它时 有些东西不起作用 我认为从下面的代码中 我只需更改以下内容 glActiveTexture GL TE
  • 如何在android中自动启动服务?

    在Android应用程序中 总是扩展Activity 入口是onCreate 所以看来用户必须选择应用程序并单击才能启动它 如果错误 请告诉我 抱歉 那么 如何实现一个无需用户点击启动应用程序而在后台运行的服务呢 使用 IntentRece
  • js-xlsx :写入 .xlsx 文件时保留单元格样式

    我已手动创建了一个 xlsx 文件 我已向某些行添加了不同的颜色 并且某些单元格具有自定义日期格式 我正在使用 js xlsx npm 模块从 xlsx 文件读取数据 在写回同一 xlsx 文件以更新某些单元格值时 所选颜色和日期格式会丢失
  • 如何为网站添加浏览器选项卡图标(favicon)?

    我一直在开发一个网站 我想在浏览器选项卡中添加一个小图标 我如何在 HTML 中执行此操作以及我需要将其放置在代码中的何处 例如标头 我有一个 png我想将其转换为图标的徽标文件 有关的 HTML 在浏览器选项卡上设置图像 https st
  • 如何创建一个持续监控应用程序使用信息的服务?

    手头的问题 我必须创建一个Service连续运行 该服务监控您手机上安装的 5 个应用程序 例如 5 个 Android 游戏 该服务需要获取以下信息 1 游戏被打开并运行了多少次 2 每场比赛的运行 时间 例如 假设我在我的应用程序中安装
  • 在选择中实现android Gmail平板电脑设计(箭头样式)

    我正在尝试构建一个平板电脑优化的应用程序 我想在选择中遵循相同风格的 gmail 平板电脑版本 选择右侧的箭头 我不知道如何获得相同的风格 有一个阴影和一个箭头可供选择 This is an image of gmail app Thank
  • 对非常大的文件进行 XSLT 转换

    我们使用 XSLT 生成数据报告 数据当前以 XML 文档的形式存储在 Oracle 中 不使用 XMLTYPE 而是普通的 CLOB 我们选择正确的 XML 文档并创建一个文档
  • Nestjs 中带有嵌套对象数组的类验证器

    我正在尝试验证 Nestjs 中的嵌套对象数组 它工作正常 但是 如果我发送非数组值 例如字符串或 null 则会收到错误 TypeError Cannot read property length of undefined 嵌套字段 Is
  • 使用 JSON 的 Django 多语言文本字段

    我最近问这个问题自定义 Django MultilingualTextField 模型字段 https stackoverflow com questions 16750509 custom django multilingualtextf