Validated codebase against pep8.

This commit is contained in:
Dirk Eschler 2010-04-19 10:52:57 +00:00
parent 411890e94b
commit 3f4a6eb472
7 changed files with 237 additions and 211 deletions

View file

@ -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

View file

@ -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

View file

@ -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()

View file

@ -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:

View file

@ -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)

View file

@ -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()

View file

@ -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('-', '_'))