Django multilingual text box using JSON

I recently asked this question the Django MultilingualTextField model field , but I did not find a good reason why I should not do this, so I create a model field that supports multilingual text, automatically returns text in the current language. This is basically the field that stores the custom Language object for the database in json format. Here is the code:

Github: 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() 

Used as a regular text field, translation is an auto-database in the system language (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 in 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') 

widgets.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 

So, I would like to know is this a good approach? Why shouldn’t I do this? Help Plz

+4
source share
1 answer

The code looks good to me.

The only thing that can affect performance is frequent json encoding / decoding ... but it should not have much impact if you do not encounter thousands of users on the server with minimal resources.

The previous question you linked contains some comments that noted that adding additional languages ​​might be easier using other tools. But, in the end, it is a mixture between personal preferences and maintainability. If this matches your project goals, I see no reason not to do it the way you encoded it.

Providing evidence that your implementation is the best is almost impossible. That is, if you do not prove it yourself by creating another, non-json version and a test version like on your production server. You will notice that on ordinary machines the differences will be minimal. However, only a few figures will provide factual evidence and help you decide if it is “configured” and “resource friendly” enough for your project goals. I think it will fit your needs ... but this is only my 2 cents.

+2
source

Source: https://habr.com/ru/post/1485729/


All Articles