mirror of
https://github.com/Hopiu/django-modeltranslation.git
synced 2026-05-12 15:23:09 +00:00
Validated codebase against pep8.
This commit is contained in:
parent
411890e94b
commit
3f4a6eb472
7 changed files with 237 additions and 211 deletions
|
|
@ -28,11 +28,14 @@ class TranslationAdminBase(object):
|
|||
# For every localized field copy the widget from the original field
|
||||
if db_field.name in trans_opts.localized_fieldnames_rev:
|
||||
orig_fieldname = trans_opts.localized_fieldnames_rev[db_field.name]
|
||||
orig_formfield = self.formfield_for_dbfield(self.model._meta.get_field(orig_fieldname), **kwargs)
|
||||
orig_formfield = self.formfield_for_dbfield( \
|
||||
self.model._meta.get_field(orig_fieldname),
|
||||
**kwargs)
|
||||
|
||||
# In case the original form field was required, make the default
|
||||
# translation field required instead.
|
||||
if db_field.language == settings.LANGUAGES[0][0] and orig_formfield.required:
|
||||
if db_field.language == settings.LANGUAGES[0][0] and \
|
||||
orig_formfield.required:
|
||||
orig_formfield.required = False
|
||||
orig_formfield.blank = True
|
||||
field.required = True
|
||||
|
|
@ -40,6 +43,7 @@ class TranslationAdminBase(object):
|
|||
|
||||
field.widget = deepcopy(orig_formfield.widget)
|
||||
|
||||
|
||||
class TranslationAdmin(admin.ModelAdmin, TranslationAdminBase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TranslationAdmin, self).__init__(*args, **kwargs)
|
||||
|
|
@ -53,7 +57,7 @@ class TranslationAdmin(admin.ModelAdmin, TranslationAdminBase):
|
|||
if field in trans_opts.fields:
|
||||
index = fields_new.index(field)
|
||||
translation_fields = get_translation_fields(field)
|
||||
fields_new[index:index+1] = translation_fields
|
||||
fields_new[index:index + 1] = translation_fields
|
||||
self.fields = fields_new
|
||||
|
||||
if self.fieldsets:
|
||||
|
|
@ -65,7 +69,7 @@ class TranslationAdmin(admin.ModelAdmin, TranslationAdminBase):
|
|||
if field in trans_opts.fields:
|
||||
index = fields_new.index(field)
|
||||
translation_fields = get_translation_fields(field)
|
||||
fields_new[index:index+1] = translation_fields
|
||||
fields_new[index:index + 1] = translation_fields
|
||||
dct['fields'] = fields_new
|
||||
self.fieldsets = fieldsets_new
|
||||
|
||||
|
|
@ -77,8 +81,9 @@ class TranslationAdmin(admin.ModelAdmin, TranslationAdminBase):
|
|||
index = editable_new.index(field)
|
||||
display_index = display_new.index(field)
|
||||
translation_fields = get_translation_fields(field)
|
||||
editable_new[index:index+1] = translation_fields
|
||||
display_new[display_index:display_index+1] = translation_fields
|
||||
editable_new[index:index + 1] = translation_fields
|
||||
display_new[display_index:display_index + 1] = \
|
||||
translation_fields
|
||||
self.list_editable = editable_new
|
||||
self.list_display = display_new
|
||||
|
||||
|
|
@ -91,7 +96,8 @@ class TranslationAdmin(admin.ModelAdmin, TranslationAdminBase):
|
|||
|
||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||
# Call the baseclass function to get the formfield
|
||||
field = super(TranslationAdmin, self).formfield_for_dbfield(db_field, **kwargs)
|
||||
field = super(TranslationAdmin, self).formfield_for_dbfield(db_field,
|
||||
**kwargs)
|
||||
self.patch_translation_field(db_field, field, **kwargs)
|
||||
return field
|
||||
|
||||
|
|
@ -99,7 +105,8 @@ class TranslationAdmin(admin.ModelAdmin, TranslationAdminBase):
|
|||
class TranslationTabularInline(admin.TabularInline, TranslationAdminBase):
|
||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||
# Call the baseclass function to get the formfield
|
||||
field = super(TranslationTabularInline, self).formfield_for_dbfield(db_field, **kwargs)
|
||||
field = super(TranslationTabularInline,
|
||||
self).formfield_for_dbfield(db_field, **kwargs)
|
||||
self.patch_translation_field(db_field, field, **kwargs)
|
||||
return field
|
||||
|
||||
|
|
@ -107,22 +114,27 @@ class TranslationTabularInline(admin.TabularInline, TranslationAdminBase):
|
|||
class TranslationStackedInline(admin.StackedInline, TranslationAdminBase):
|
||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||
# Call the baseclass function to get the formfield
|
||||
field = super(TranslationStackedInline, self).formfield_for_dbfield(db_field, **kwargs)
|
||||
field = super(TranslationStackedInline,
|
||||
self).formfield_for_dbfield(db_field, **kwargs)
|
||||
self.patch_translation_field(db_field, field, **kwargs)
|
||||
return field
|
||||
|
||||
|
||||
class TranslationGenericTabularInline(generic.GenericTabularInline, TranslationAdminBase):
|
||||
class TranslationGenericTabularInline(generic.GenericTabularInline,
|
||||
TranslationAdminBase):
|
||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||
# Call the baseclass function to get the formfield
|
||||
field = super(TranslationGenericTabularInline, self).formfield_for_dbfield(db_field, **kwargs)
|
||||
field = super(TranslationGenericTabularInline,
|
||||
self).formfield_for_dbfield(db_field, **kwargs)
|
||||
self.patch_translation_field(db_field, field, **kwargs)
|
||||
return field
|
||||
|
||||
|
||||
class TranslationGenericStackedInline(generic.GenericStackedInline, TranslationAdminBase):
|
||||
class TranslationGenericStackedInline(generic.GenericStackedInline,
|
||||
TranslationAdminBase):
|
||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||
# Call the baseclass function to get the formfield
|
||||
field = super(TranslationGenericStackedInline, self).formfield_for_dbfield(db_field, **kwargs)
|
||||
field = super(TranslationGenericStackedInline,
|
||||
self).formfield_for_dbfield(db_field, **kwargs)
|
||||
self.patch_translation_field(db_field, field, **kwargs)
|
||||
return field
|
||||
|
|
|
|||
|
|
@ -4,11 +4,12 @@ from django.db.models.fields import Field, CharField
|
|||
|
||||
from modeltranslation.utils import get_language, build_localized_fieldname
|
||||
|
||||
|
||||
class TranslationField(Field):
|
||||
"""
|
||||
The translation field functions as a proxy to the original field which is
|
||||
wrapped.
|
||||
|
||||
wrapped.
|
||||
|
||||
For every field defined in the model's ``TranslationOptions`` localized
|
||||
versions of that field are added to the model depending on the languages
|
||||
given in ``settings.LANGUAGES``.
|
||||
|
|
@ -16,55 +17,56 @@ class TranslationField(Field):
|
|||
If for example there is a model ``News`` with a field ``title`` which is
|
||||
registered for translation and the ``settings.LANGUAGES`` contains the
|
||||
``de`` and ``en`` languages, the fields ``title_de`` and ``title_en`` will
|
||||
be added to the model class. These fields are realized using this
|
||||
be added to the model class. These fields are realized using this
|
||||
descriptor.
|
||||
|
||||
|
||||
The translation field needs to know which language it contains therefore
|
||||
that needs to be specified when the field is created.
|
||||
|
||||
"""
|
||||
def __init__(self, translated_field, language, *args, **kwargs):
|
||||
# Store the originally wrapped field for later
|
||||
self.translated_field = translated_field
|
||||
self.language = language
|
||||
|
||||
|
||||
# Update the dict of this field with the content of the original one
|
||||
# This might be a bit radical?! Seems to work though...
|
||||
self.__dict__.update(translated_field.__dict__)
|
||||
|
||||
self.__dict__.update(translated_field.__dict__)
|
||||
|
||||
# Translation are always optional (for now - maybe add some parameters
|
||||
# to the translation options for configuring this)
|
||||
self.null = True
|
||||
self.blank = True
|
||||
|
||||
|
||||
# Adjust the name of this field to reflect the language
|
||||
self.attname = build_localized_fieldname(translated_field.name,
|
||||
language)
|
||||
self.name = self.attname
|
||||
|
||||
|
||||
# Copy the verbose name and append a language suffix (will e.g. in the
|
||||
# admin). This might be a proxy function so we have to check that here.
|
||||
if hasattr(translated_field.verbose_name, '_proxy____unicode_cast'):
|
||||
verbose_name = translated_field.verbose_name._proxy____unicode_cast()
|
||||
if hasattr(translated_field.verbose_name, '_proxy____unicode_cast'):
|
||||
verbose_name = \
|
||||
translated_field.verbose_name._proxy____unicode_cast()
|
||||
else:
|
||||
verbose_name = translated_field.verbose_name
|
||||
verbose_name = translated_field.verbose_name
|
||||
self.verbose_name = '%s [%s]' % (verbose_name, language)
|
||||
|
||||
def pre_save(self, model_instance, add):
|
||||
|
||||
def pre_save(self, model_instance, add):
|
||||
val = super(TranslationField, self).pre_save(model_instance, add)
|
||||
if get_language() == self.language and not add:
|
||||
if get_language() == self.language and not add:
|
||||
# Rule is: 3. Assigning a value to a translation field of the
|
||||
# default language also updates the original field
|
||||
model_instance.__dict__[self.translated_field.name] = val
|
||||
return val
|
||||
|
||||
|
||||
def get_internal_type(self):
|
||||
return self.translated_field.get_internal_type()
|
||||
|
||||
#def contribute_to_class(self, cls, name):
|
||||
|
||||
#def contribute_to_class(self, cls, name):
|
||||
#super(TranslationField, self).contribute_to_class(cls, name)
|
||||
##setattr(cls, 'get_%s_display' % self.name, curry(cls._get_FIELD_display, field=self))
|
||||
|
||||
##setattr(cls, 'get_%s_display' % self.name,
|
||||
##curry(cls._get_FIELD_display, field=self))
|
||||
|
||||
def south_field_triple(self):
|
||||
"""Returns a suitable description of this field for South."""
|
||||
# We'll just introspect the _actual_ field.
|
||||
|
|
@ -74,7 +76,7 @@ class TranslationField(Field):
|
|||
args, kwargs = introspector(self.translated_field)
|
||||
# That's our definition!
|
||||
return (field_class, args, kwargs)
|
||||
|
||||
|
||||
def formfield(self, *args, **kwargs):
|
||||
"""Preserves the widget of the translated field."""
|
||||
trans_formfield = self.translated_field.formfield(*args, **kwargs)
|
||||
|
|
@ -85,24 +87,24 @@ class TranslationField(Field):
|
|||
|
||||
#class CurrentLanguageField(CharField):
|
||||
#def __init__(self, **kwargs):
|
||||
#super(CurrentLanguageField, self).__init__(null=True, max_length=5, **kwargs)
|
||||
|
||||
#super(CurrentLanguageField, self).__init__(null=True, max_length=5,
|
||||
#**kwargs)
|
||||
|
||||
#def contribute_to_class(self, cls, name):
|
||||
#super(CurrentLanguageField, self).contribute_to_class(cls, name)
|
||||
#registry = CurrentLanguageFieldRegistry()
|
||||
#registry.add_field(cls, self)
|
||||
|
||||
|
||||
|
||||
|
||||
#class CurrentLanguageFieldRegistry(object):
|
||||
#_registry = {}
|
||||
|
||||
|
||||
#def add_field(self, model, field):
|
||||
#reg = self.__class__._registry.setdefault(model, [])
|
||||
#reg.append(field)
|
||||
|
||||
|
||||
#def get_fields(self, model):
|
||||
#return self.__class__._registry.get(model, [])
|
||||
|
||||
|
||||
#def __contains__(self, model):
|
||||
#return model in self.__class__._registry
|
||||
|
||||
|
|
@ -1,24 +1,30 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from django.conf import settings
|
||||
from django.core.management.base import BaseCommand, CommandError, NoArgsCommand
|
||||
from django.core.management.base import (BaseCommand, CommandError,
|
||||
NoArgsCommand)
|
||||
|
||||
from modeltranslation.translator import translator
|
||||
from modeltranslation.utils import build_localized_fieldname
|
||||
|
||||
|
||||
class Command(NoArgsCommand):
|
||||
help = 'Updates the default translation fields of all or the specified' \
|
||||
'translated application using the value of the original field.'
|
||||
|
||||
def handle(self, **options):
|
||||
default_lang = settings.LANGUAGES[0][0]
|
||||
print "Using default language:", default_lang
|
||||
|
||||
for model, trans_opts in translator._registry.items():
|
||||
print "Updating data of model '%s'" % model
|
||||
for obj in model.objects.all():
|
||||
|
||||
def handle(self, **options):
|
||||
default_lang = settings.LANGUAGES[0][0]
|
||||
print "Using default language:", default_lang
|
||||
|
||||
for model, trans_opts in translator._registry.items():
|
||||
print "Updating data of model '%s'" % model
|
||||
for obj in model.objects.all():
|
||||
for fieldname in trans_opts.fields:
|
||||
def_lang_fieldname = build_localized_fieldname(fieldname, default_lang)
|
||||
# print "setting %s from %s to %s." % (def_lang_fieldname, fieldname, obj.__dict__[fieldname])
|
||||
def_lang_fieldname = \
|
||||
build_localized_fieldname(fieldname, default_lang)
|
||||
#print "setting %s from %s to %s." % \
|
||||
#(def_lang_fieldname, fieldname,
|
||||
#obj.__dict__[fieldname])
|
||||
if not getattr(obj, def_lang_fieldname):
|
||||
setattr(obj, def_lang_fieldname, obj.__dict__[fieldname])
|
||||
setattr(obj, def_lang_fieldname,
|
||||
obj.__dict__[fieldname])
|
||||
obj.save()
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@ from django.db import models
|
|||
from modeltranslation.translator import translator
|
||||
|
||||
# Every model registered with the modeltranslation.translator.translator
|
||||
# is patched to contain additional localized versions for every
|
||||
# is patched to contain additional localized versions for every
|
||||
# field specified in the model's translation options.
|
||||
|
||||
# Import the project's global "translation.py" which registers model
|
||||
# Import the project's global "translation.py" which registers model
|
||||
# classes and their translation options with the translator object.
|
||||
if getattr(settings, 'TRANSLATION_REGISTRY', False):
|
||||
try:
|
||||
try:
|
||||
__import__(settings.TRANSLATION_REGISTRY, {}, {}, [''])
|
||||
except ImportError:
|
||||
sys.stderr.write("modeltranslation: Can't import module '%s'.\n"
|
||||
|
|
@ -27,7 +27,7 @@ if getattr(settings, 'TRANSLATION_REGISTRY', False):
|
|||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
# After importing all translation modules, all translation classes are
|
||||
# After importing all translation modules, all translation classes are
|
||||
# registered with the translator.
|
||||
if settings.DEBUG:
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -29,54 +29,52 @@ translator.translator._registry = {}
|
|||
translator.translator.register(TestModel, TestTranslationOptions)
|
||||
|
||||
|
||||
class ModelTranslationTest(TestCase):
|
||||
class ModelTranslationTest(TestCase):
|
||||
"""Basic tests for the modeltranslation application."""
|
||||
|
||||
urls = 'modeltranslation.testurls'
|
||||
|
||||
def setUp(self):
|
||||
|
||||
def setUp(self):
|
||||
trans_real.activate("de")
|
||||
|
||||
|
||||
def tearDown(self):
|
||||
trans_real.deactivate()
|
||||
|
||||
|
||||
def test_registration(self):
|
||||
self.client.post('/set_language/', data={'language': 'de'})
|
||||
#self.client.session['django_language'] = 'de-de'
|
||||
#self.client.session['django_language'] = 'de-de'
|
||||
#self.client.cookies[settings.LANGUAGE_COOKIE_NAME] = 'de-de'
|
||||
|
||||
|
||||
langs = tuple(l[0] for l in settings.LANGUAGES)
|
||||
self.failUnlessEqual(2, len(langs))
|
||||
self.failUnless('de' in langs)
|
||||
self.failUnless('en' in langs)
|
||||
self.failUnless('en' in langs)
|
||||
self.failUnless(translator.translator)
|
||||
|
||||
# Check that only one model is registered for translation
|
||||
self.failUnlessEqual(len(translator.translator._registry), 1)
|
||||
|
||||
|
||||
# Try to unregister a model that is not registered
|
||||
self.assertRaises(translator.NotRegistered,
|
||||
translator.translator.unregister, User)
|
||||
|
||||
|
||||
# Try to get options for a model that is not registered
|
||||
self.assertRaises(translator.NotRegistered,
|
||||
translator.translator.get_options_for_model, User)
|
||||
|
||||
def test_translated_models(self):
|
||||
|
||||
def test_translated_models(self):
|
||||
# First create an instance of the test model to play with
|
||||
inst = TestModel.objects.create(title="Testtitle", text="Testtext")
|
||||
field_names = dir(inst)
|
||||
self.failUnless('id' in field_names)
|
||||
self.failUnless('title' in field_names)
|
||||
self.failUnless('text' in field_names)
|
||||
self.failUnless('title_de' in field_names)
|
||||
self.failUnless('title_en' in field_names)
|
||||
self.failUnless('text_de' in field_names)
|
||||
self.failUnless('text_en' in field_names)
|
||||
|
||||
self.failUnless('title_de' in field_names)
|
||||
self.failUnless('title_en' in field_names)
|
||||
self.failUnless('text_de' in field_names)
|
||||
self.failUnless('text_en' in field_names)
|
||||
|
||||
inst.delete()
|
||||
|
||||
|
||||
def test_set_translation(self):
|
||||
self.failUnlessEqual(get_language(), "de")
|
||||
# First create an instance of the test model to play with
|
||||
|
|
@ -88,55 +86,55 @@ class ModelTranslationTest(TestCase):
|
|||
inst2 = TestModel(title=title2_de, text="Testtext")
|
||||
inst1.save()
|
||||
inst2.save()
|
||||
|
||||
|
||||
self.failUnlessEqual(inst1.title, title1_de)
|
||||
self.failUnlessEqual(inst1.title_en, title1_en)
|
||||
|
||||
|
||||
self.failUnlessEqual(inst2.title, title2_de)
|
||||
self.failUnlessEqual(inst2.title_en, None)
|
||||
|
||||
self.failUnlessEqual(inst2.title_en, None)
|
||||
|
||||
del inst1
|
||||
del inst2
|
||||
|
||||
|
||||
# Check that the translation fields are correctly saved and provide the
|
||||
# correct value when retrieving them again.
|
||||
n = TestModel.objects.get(title=title1_de)
|
||||
self.failUnlessEqual(n.title, title1_de)
|
||||
self.failUnlessEqual(n.title_en, title1_en)
|
||||
|
||||
|
||||
def test_titleonly(self):
|
||||
title1_de = "title de"
|
||||
n = TestModel.objects.create(title=title1_de)
|
||||
self.failUnlessEqual(n.title, title1_de)
|
||||
# Because the original field "title" was specified in the constructor
|
||||
# it is directly passed into the instance's __dict__ and the descriptor
|
||||
# Because the original field "title" was specified in the constructor
|
||||
# it is directly passed into the instance's __dict__ and the descriptor
|
||||
# which updates the associated default translation field is not called
|
||||
# and the default translation will be None.
|
||||
self.failUnlessEqual(n.title_de, None)
|
||||
self.failUnlessEqual(n.title_en, None)
|
||||
|
||||
|
||||
# Now assign the title, that triggers the descriptor and the default
|
||||
# translation field is updated
|
||||
n.title = title1_de
|
||||
self.failUnlessEqual(n.title, title1_de)
|
||||
self.failUnlessEqual(n.title_de, title1_de)
|
||||
self.failUnlessEqual(n.title_en, None)
|
||||
|
||||
def test_rule1(self):
|
||||
|
||||
def test_rule1(self):
|
||||
"""
|
||||
Rule 1: Reading the value from the original field returns the value in
|
||||
Rule 1: Reading the value from the original field returns the value in
|
||||
translated to the current language.
|
||||
"""
|
||||
title1_de = "title de"
|
||||
title1_en = "title en"
|
||||
text_de = "Dies ist ein deutscher Satz"
|
||||
text_en = "This is an english sentence"
|
||||
|
||||
|
||||
# Test 1.
|
||||
n = TestModel.objects.create(title_de=title1_de, title_en=title1_en,
|
||||
text_de=text_de, text_en=text_en)
|
||||
n.save()
|
||||
|
||||
|
||||
# language is set to "de" at this point
|
||||
self.failUnlessEqual(get_language(), "de")
|
||||
self.failUnlessEqual(n.title, title1_de)
|
||||
|
|
@ -150,9 +148,9 @@ class ModelTranslationTest(TestCase):
|
|||
self.failUnlessEqual(get_language(), "en")
|
||||
# Title should now be return the english one (just by switching the
|
||||
# language)
|
||||
self.failUnlessEqual(n.title, title1_en)
|
||||
self.failUnlessEqual(n.text, text_en)
|
||||
|
||||
self.failUnlessEqual(n.title, title1_en)
|
||||
self.failUnlessEqual(n.text, text_en)
|
||||
|
||||
n = TestModel.objects.create(title_de=title1_de, title_en=title1_en,
|
||||
text_de=text_de, text_en=text_en)
|
||||
n.save()
|
||||
|
|
@ -168,8 +166,8 @@ class ModelTranslationTest(TestCase):
|
|||
self.failUnlessEqual(n.title, title1_de)
|
||||
self.failUnlessEqual(n.text, text_de)
|
||||
trans_real.deactivate()
|
||||
|
||||
def test_rule2(self):
|
||||
|
||||
def test_rule2(self):
|
||||
"""
|
||||
Rule 2: Assigning a value to the original field also updates the value
|
||||
in the associated translation field of the default language
|
||||
|
|
@ -177,48 +175,48 @@ class ModelTranslationTest(TestCase):
|
|||
self.failUnlessEqual(get_language(), "de")
|
||||
title1_de = "title de"
|
||||
title1_en = "title en"
|
||||
n = TestModel.objects.create(title_de=title1_de, title_en=title1_en)
|
||||
n = TestModel.objects.create(title_de=title1_de, title_en=title1_en)
|
||||
self.failUnlessEqual(n.title, title1_de)
|
||||
self.failUnlessEqual(n.title_de, title1_de)
|
||||
self.failUnlessEqual(n.title_en, title1_en)
|
||||
|
||||
title2 = "Neuer Titel"
|
||||
|
||||
title2 = "Neuer Titel"
|
||||
n.title = title2
|
||||
n.save()
|
||||
self.failUnlessEqual(n.title, title2)
|
||||
self.failUnlessEqual(n.title, n.title_de)
|
||||
|
||||
|
||||
trans_real.activate("en")
|
||||
self.failUnlessEqual(get_language(), "en")
|
||||
title3 = "new title"
|
||||
|
||||
|
||||
n.title = title3
|
||||
n.title_de = title1_de
|
||||
n.save()
|
||||
self.failUnlessEqual(n.title, title3)
|
||||
self.failUnlessEqual(n.title, n.title_en)
|
||||
self.failUnlessEqual(title1_de, n.title_de)
|
||||
|
||||
trans_real.deactivate()
|
||||
|
||||
|
||||
trans_real.deactivate()
|
||||
|
||||
def test_rule3(self):
|
||||
"""
|
||||
Rule 3: Assigning a value to a translation field of the default language
|
||||
also updates the original field - note that the value of the original
|
||||
field will not be updated until the model instance is saved.
|
||||
Rule 3: Assigning a value to a translation field of the default
|
||||
language also updates the original field - note that the value of the
|
||||
original field will not be updated until the model instance is saved.
|
||||
"""
|
||||
title1_de = "title de"
|
||||
title1_en = "title en"
|
||||
n = TestModel.objects.create(title_de=title1_de, title_en=title1_en)
|
||||
n = TestModel.objects.create(title_de=title1_de, title_en=title1_en)
|
||||
self.failUnlessEqual(get_language(), "de")
|
||||
self.failUnlessEqual(n.title, title1_de)
|
||||
self.failUnlessEqual(n.title_de, title1_de)
|
||||
self.failUnlessEqual(n.title_en, title1_en)
|
||||
|
||||
|
||||
n.title_de = "Neuer Titel"
|
||||
n.save()
|
||||
self.failUnlessEqual(n.title, n.title_de)
|
||||
|
||||
|
||||
# Now switch to "en"
|
||||
trans_real.activate("en")
|
||||
self.failUnlessEqual(get_language(), "en")
|
||||
|
|
@ -227,21 +225,21 @@ class ModelTranslationTest(TestCase):
|
|||
n.save()
|
||||
self.failUnlessEqual(n.title, n.title_en)
|
||||
trans_real.deactivate()
|
||||
|
||||
def test_rule4(self):
|
||||
|
||||
def test_rule4(self):
|
||||
"""
|
||||
Rule 4: If both fields - the original and the translation field of the
|
||||
default language - are updated at the same time, the translation field
|
||||
Rule 4: If both fields - the original and the translation field of the
|
||||
default language - are updated at the same time, the translation field
|
||||
wins.
|
||||
"""
|
||||
self.failUnlessEqual(get_language(), "de")
|
||||
title1_de = "title de"
|
||||
title1_en = "title en"
|
||||
n = TestModel.objects.create(title_de=title1_de, title_en=title1_en)
|
||||
n = TestModel.objects.create(title_de=title1_de, title_en=title1_en)
|
||||
self.failUnlessEqual(n.title, title1_de)
|
||||
self.failUnlessEqual(n.title_de, title1_de)
|
||||
self.failUnlessEqual(n.title_en, title1_en)
|
||||
|
||||
|
||||
title2_de = "neu de"
|
||||
title2_en = "new en"
|
||||
title_foo = "foo"
|
||||
|
|
@ -252,10 +250,9 @@ class ModelTranslationTest(TestCase):
|
|||
self.failUnlessEqual(n.title, title2_de)
|
||||
self.failUnlessEqual(n.title_de, title2_de)
|
||||
self.failUnlessEqual(n.title_en, title2_en)
|
||||
|
||||
|
||||
n.title = title_foo
|
||||
n.save()
|
||||
self.failUnlessEqual(n.title, title_foo)
|
||||
self.failUnlessEqual(n.title_de, title_foo)
|
||||
self.failUnlessEqual(n.title_en, title2_en)
|
||||
|
||||
|
|
|
|||
|
|
@ -6,12 +6,14 @@ from django.db.models import signals
|
|||
from django.db.models.base import ModelBase
|
||||
from django.utils.functional import curry
|
||||
|
||||
from modeltranslation.fields import TranslationField
|
||||
from modeltranslation.utils import TranslationFieldDescriptor, build_localized_fieldname
|
||||
from modeltranslation.fields import TranslationField
|
||||
from modeltranslation.utils import (TranslationFieldDescriptor,
|
||||
build_localized_fieldname)
|
||||
|
||||
|
||||
class AlreadyRegistered(Exception):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class NotRegistered(Exception):
|
||||
pass
|
||||
|
|
@ -20,10 +22,10 @@ class NotRegistered(Exception):
|
|||
class TranslationOptions(object):
|
||||
"""
|
||||
The TranslationOptions object is used to specify the fields to translate.
|
||||
|
||||
|
||||
The options are registered in combination with a model class at the
|
||||
``modeltranslation.translator.translator`` instance.
|
||||
|
||||
|
||||
It caches the content type of the translated model for faster lookup later
|
||||
on.
|
||||
"""
|
||||
|
|
@ -35,59 +37,61 @@ class TranslationOptions(object):
|
|||
|
||||
def add_localized_fields(model):
|
||||
"""
|
||||
Monkey patchs the original model class to provide additional fields for
|
||||
every language. Only do that for fields which are defined in the
|
||||
Monkey patchs the original model class to provide additional fields for
|
||||
every language. Only do that for fields which are defined in the
|
||||
translation options of the model.
|
||||
|
||||
|
||||
Returns a dict mapping the original fieldname to a list containing the
|
||||
names of the localized fields created for the original field.
|
||||
"""
|
||||
localized_fields = dict()
|
||||
translation_opts = translator.get_options_for_model(model)
|
||||
for field_name in translation_opts.fields:
|
||||
for field_name in translation_opts.fields:
|
||||
localized_fields[field_name] = list()
|
||||
for l in settings.LANGUAGES:
|
||||
for l in settings.LANGUAGES:
|
||||
# Construct the name for the localized field
|
||||
localized_field_name = build_localized_fieldname(field_name, l[0])
|
||||
# Check if the model already has a field by that name
|
||||
# Check if the model already has a field by that name
|
||||
if hasattr(model, localized_field_name):
|
||||
raise ValueError("Error adding translation field. The model "\
|
||||
"'%s' already contains a field named '%s'. "\
|
||||
% (instance.__class__.__name__,
|
||||
localized_field_name))
|
||||
|
||||
|
||||
# This approach implements the translation fields as full valid
|
||||
# django model fields and therefore adds them via add_to_class
|
||||
localized_field = model.add_to_class(localized_field_name,
|
||||
TranslationField(model._meta.get_field(field_name),
|
||||
l[0]))
|
||||
localized_field = model.add_to_class( \
|
||||
localized_field_name,
|
||||
TranslationField(model._meta.get_field(field_name), l[0]))
|
||||
localized_fields[field_name].append(localized_field_name)
|
||||
return localized_fields
|
||||
|
||||
|
||||
return localized_fields
|
||||
|
||||
|
||||
#def translated_model_initialized(field_names, instance, **kwargs):
|
||||
#print "translated_model_initialized instance:", instance, ", field:", field_names
|
||||
#print "translated_model_initialized instance:", \
|
||||
#instance, ", field:", field_names
|
||||
#for field_name in field_names:
|
||||
#initial_val = getattr(instance, field_name)
|
||||
#print " field: %s, initialval: %s" % (field_name, initial_val)
|
||||
#setattr(instance.__class__, field_name, TranslationFieldDescriptor(field_name,
|
||||
#initial_val))
|
||||
|
||||
|
||||
#setattr(instance.__class__, field_name,
|
||||
#TranslationFieldDescriptor(field_name, initial_val))
|
||||
|
||||
|
||||
#def translated_model_initializing(sender, args, kwargs, **signal_kwargs):
|
||||
#print "translated_model_initializing", sender, args, kwargs
|
||||
#print "translated_model_initializing", sender, args, kwargs
|
||||
#trans_opts = translator.get_options_for_model(sender)
|
||||
#for field_name in trans_opts.fields:
|
||||
#setattr(sender, field_name, TranslationFieldDescriptor(field_name))
|
||||
|
||||
|
||||
|
||||
|
||||
class Translator(object):
|
||||
"""
|
||||
A Translator object encapsulates an instance of a translator. Models are
|
||||
registered with the Translator using the register() method.
|
||||
"""
|
||||
def __init__(self):
|
||||
self._registry = {} # model_class class -> translation_opts instance
|
||||
# model_class class -> translation_opts instance
|
||||
self._registry = {}
|
||||
|
||||
def register(self, model_or_iterable, translation_opts, **options):
|
||||
"""
|
||||
|
|
@ -95,7 +99,7 @@ class Translator(object):
|
|||
|
||||
The model(s) should be Model classes, not instances.
|
||||
|
||||
If a model is already registered for translation, this will raise
|
||||
If a model is already registered for translation, this will raise
|
||||
AlreadyRegistered.
|
||||
"""
|
||||
# Don't import the humongous validation code unless required
|
||||
|
|
@ -105,13 +109,14 @@ class Translator(object):
|
|||
validate = lambda model, adminclass: None
|
||||
|
||||
#if not translation_opts:
|
||||
#translation_opts = TranslationOptions
|
||||
#translation_opts = TranslationOptions
|
||||
if isinstance(model_or_iterable, ModelBase):
|
||||
model_or_iterable = [model_or_iterable]
|
||||
|
||||
|
||||
for model in model_or_iterable:
|
||||
if model in self._registry:
|
||||
raise AlreadyRegistered('The model %s is already registered for translation' % model.__name__)
|
||||
raise AlreadyRegistered('The model %s is already registered '
|
||||
'for translation' % model.__name__)
|
||||
|
||||
# If we got **options then dynamically construct a subclass of
|
||||
# translation_opts with those **options.
|
||||
|
|
@ -120,63 +125,67 @@ class Translator(object):
|
|||
# the created class appears to "live" in the wrong place,
|
||||
# which causes issues later on.
|
||||
options['__module__'] = __name__
|
||||
translation_opts = type("%sAdmin" % model.__name__, (translation_opts,), options)
|
||||
translation_opts = type("%sAdmin" % model.__name__,
|
||||
(translation_opts,), options)
|
||||
|
||||
# Validate (which might be a no-op)
|
||||
#validate(translation_opts, model)
|
||||
|
||||
# Store the translation class associated to the model
|
||||
self._registry[model] = translation_opts
|
||||
|
||||
|
||||
# Get the content type of the original model and store it on the
|
||||
# translation options for faster lookup later on.
|
||||
#translation_opts.model_ct = ContentType.objects.get_for_model(model)
|
||||
|
||||
# Add the localized fields to the model and store the names of these
|
||||
# fields in the model's translation options for faster lookup later
|
||||
# on.
|
||||
#translation_opts.model_ct = \
|
||||
#ContentType.objects.get_for_model(model)
|
||||
|
||||
# Add the localized fields to the model and store the names of
|
||||
# these fields in the model's translation options for faster lookup
|
||||
# later on.
|
||||
translation_opts.localized_fieldnames = add_localized_fields(model)
|
||||
|
||||
# Create a reverse dict mapping the localized_fieldnames to the
|
||||
|
||||
# Create a reverse dict mapping the localized_fieldnames to the
|
||||
# original fieldname
|
||||
rev_dict = dict()
|
||||
for orig_name, loc_names in translation_opts.localized_fieldnames.items():
|
||||
for orig_name, loc_names in \
|
||||
translation_opts.localized_fieldnames.items():
|
||||
for ln in loc_names:
|
||||
rev_dict[ln] = orig_name
|
||||
|
||||
translation_opts.localized_fieldnames_rev = rev_dict
|
||||
|
||||
# print "Applying descriptor field for model %s" % model
|
||||
|
||||
translation_opts.localized_fieldnames_rev = rev_dict
|
||||
|
||||
# print "Applying descriptor field for model %s" % model
|
||||
for field_name in translation_opts.fields:
|
||||
setattr(model, field_name, TranslationFieldDescriptor(field_name))
|
||||
|
||||
#signals.pre_init.connect(translated_model_initializing, sender=model, weak=False)
|
||||
|
||||
|
||||
#signals.pre_init.connect(translated_model_initializing, sender=model,
|
||||
#weak=False)
|
||||
|
||||
def unregister(self, model_or_iterable):
|
||||
"""
|
||||
Unregisters the given model(s).
|
||||
|
||||
If a model isn't already registered, this will raise NotRegistered.
|
||||
|
||||
"""
|
||||
if isinstance(model_or_iterable, ModelBase):
|
||||
model_or_iterable = [model_or_iterable]
|
||||
for model in model_or_iterable:
|
||||
if model not in self._registry:
|
||||
raise NotRegistered('The model "%s" is not registered for translation' % model.__name__)
|
||||
raise NotRegistered('The model "%s" is not registered for '
|
||||
'translation' % model.__name__)
|
||||
del self._registry[model]
|
||||
|
||||
|
||||
def get_options_for_model(self, model):
|
||||
"""
|
||||
Returns the translation options for the given ``model``. If the
|
||||
Returns the translation options for the given ``model``. If the
|
||||
``model`` is not registered a ``NotRegistered`` exception is raised.
|
||||
|
||||
"""
|
||||
try:
|
||||
return self._registry[model]
|
||||
except KeyError:
|
||||
raise NotRegistered('The model "%s" is not registered for translation' % model.__name__)
|
||||
|
||||
raise NotRegistered('The model "%s" is not registered for '
|
||||
'translation' % model.__name__)
|
||||
|
||||
|
||||
# This global object represents the singleton translator object
|
||||
translator = Translator()
|
||||
|
|
|
|||
|
|
@ -5,11 +5,12 @@ from django.core.exceptions import ValidationError
|
|||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.utils.translation import get_language as _get_language
|
||||
|
||||
|
||||
def get_language():
|
||||
"""
|
||||
Return an active language code that is guaranteed to be in
|
||||
settings.LANGUAGES (Django does not seem to guarantee this for us.)
|
||||
|
||||
|
||||
"""
|
||||
lang = _get_language()
|
||||
available_languages = [l[0] for l in settings.LANGUAGES]
|
||||
|
|
@ -25,6 +26,10 @@ def get_translation_fields(field):
|
|||
return [build_localized_fieldname(field, l[0]) for l in settings.LANGUAGES]
|
||||
|
||||
|
||||
def build_localized_fieldname(field_name, lang):
|
||||
return '%s_%s' % (field_name, lang.replace('-', '_'))
|
||||
|
||||
|
||||
class TranslationFieldDescriptor(object):
|
||||
"""A descriptor used for the original translated field."""
|
||||
def __init__(self, name, initial_val=""):
|
||||
|
|
@ -32,42 +37,40 @@ class TranslationFieldDescriptor(object):
|
|||
The ``name`` is the name of the field (which is not available in the
|
||||
descriptor by default - this is Python behaviour).
|
||||
"""
|
||||
self.name = name
|
||||
self.name = name
|
||||
self.val = initial_val
|
||||
|
||||
def __set__(self, instance, value):
|
||||
# print "Descriptor.__set__%s %s %s.%s: %s" % (id(instance), id(self), type(instance), self.name, value)
|
||||
def __set__(self, instance, value):
|
||||
lang = get_language()
|
||||
loc_field_name = build_localized_fieldname(self.name, lang)
|
||||
|
||||
# also update the translation field of the current language
|
||||
# also update the translation field of the current language
|
||||
setattr(instance, loc_field_name, value)
|
||||
|
||||
# update the original field via the __dict__ to prevent calling the
|
||||
# descriptor
|
||||
instance.__dict__[self.name] = value
|
||||
|
||||
def __get__(self, instance, owner):
|
||||
# print "Descriptor.__get__%s %s %s.%s: %s" % (id(instance), id(self), type(instance), self.name, self.val)
|
||||
if not instance:
|
||||
raise ValueError(u"Translation field '%s' can only be "\
|
||||
"accessed via an instance not via "\
|
||||
"a class." % self.name)
|
||||
|
||||
lang = get_language()
|
||||
loc_field_name = build_localized_fieldname(self.name, lang)
|
||||
if hasattr(instance, loc_field_name):
|
||||
return getattr(instance, loc_field_name) or instance.__dict__[self.name]
|
||||
return instance.__dict__[self.name]
|
||||
|
||||
|
||||
#def create_model(name, fields=None, app_label='', module='', options=None, admin_opts=None):
|
||||
def __get__(self, instance, owner):
|
||||
if not instance:
|
||||
raise ValueError(u"Translation field '%s' can only be "
|
||||
"accessed via an instance not via "
|
||||
"a class." % self.name)
|
||||
lang = get_language()
|
||||
loc_field_name = build_localized_fieldname(self.name, lang)
|
||||
if hasattr(instance, loc_field_name):
|
||||
return getattr(instance, loc_field_name) or \
|
||||
instance.__dict__[self.name]
|
||||
return instance.__dict__[self.name]
|
||||
|
||||
|
||||
#def create_model(name, fields=None, app_label='', module='', options=None,
|
||||
#admin_opts=None):
|
||||
#"""
|
||||
#Create specified model.
|
||||
#This is taken from http://code.djangoproject.com/wiki/DynamicModels
|
||||
#"""
|
||||
#class Meta:
|
||||
## Using type('Meta', ...) gives a dictproxy error during model creation
|
||||
## Using type('Meta', ...) gives a dictproxy error during model
|
||||
## creation
|
||||
#pass
|
||||
|
||||
#if app_label:
|
||||
|
|
@ -98,26 +101,23 @@ class TranslationFieldDescriptor(object):
|
|||
#admin.site.register(model, Admin)
|
||||
|
||||
#return model
|
||||
|
||||
|
||||
|
||||
|
||||
def copy_field(field):
|
||||
"""
|
||||
Instantiate a new field, with all of the values from the old one, except the
|
||||
to and to_field in the case of related fields.
|
||||
|
||||
Instantiate a new field, with all of the values from the old one, except
|
||||
the to and to_field in the case of related fields.
|
||||
|
||||
This taken from http://www.djangosnippets.org/snippets/442/
|
||||
|
||||
"""
|
||||
base_kw = dict([(n, getattr(field, n, '_null')) for n in models.fields.Field.__init__.im_func.func_code.co_varnames])
|
||||
"""
|
||||
base_kw = dict([(n, getattr(field, n, '_null')) for n in \
|
||||
models.fields.Field.__init__.im_func.func_code.co_varnames])
|
||||
if isinstance(field, models.fields.related.RelatedField):
|
||||
rel = base_kw.get('rel')
|
||||
rel_kw = dict([(n, getattr(rel, n, '_null')) for n in rel.__init__.im_func.func_code.co_varnames])
|
||||
rel_kw = dict([(n, getattr(rel, n, '_null')) for n in \
|
||||
rel.__init__.im_func.func_code.co_varnames])
|
||||
if isinstance(field, models.fields.related.ForeignKey):
|
||||
base_kw['to_field'] = rel_kw.pop('field_name')
|
||||
base_kw.update(rel_kw)
|
||||
base_kw.pop('self')
|
||||
base_kw.pop('self')
|
||||
return field.__class__(**base_kw)
|
||||
|
||||
|
||||
def build_localized_fieldname(field_name, lang):
|
||||
return '%s_%s' % (field_name, lang.replace('-', '_'))
|
||||
|
|
|
|||
Loading…
Reference in a new issue