mirror of
https://github.com/Hopiu/django-modeltranslation.git
synced 2026-04-09 08:10:57 +00:00
Add MultilingualManager and its tests.
This commit is contained in:
parent
3868e461e8
commit
0949e95441
8 changed files with 397 additions and 7 deletions
146
modeltranslation/manager.py
Normal file
146
modeltranslation/manager.py
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
"""
|
||||
The idea of MultilingualManager is taken from
|
||||
django-linguo by Zach Mathew
|
||||
|
||||
https://github.com/zmathew/django-linguo
|
||||
"""
|
||||
from django.db import models
|
||||
from django.db.models.fields.related import RelatedField
|
||||
from django.utils.translation import get_language
|
||||
from django.utils.tree import Node
|
||||
|
||||
from modeltranslation.utils import build_localized_fieldname
|
||||
from modeltranslation import settings
|
||||
|
||||
|
||||
_registry = {}
|
||||
|
||||
|
||||
def get_translatable_fields_for_model(model):
|
||||
from modeltranslation import translator
|
||||
if model not in _registry:
|
||||
try:
|
||||
_registry[model] = dict(
|
||||
translator.translator.get_options_for_model(model).localized_fieldnames)
|
||||
except translator.NotRegistered:
|
||||
_registry[model] = None
|
||||
return _registry[model]
|
||||
|
||||
|
||||
def rewrite_lookup_key(model, lookup_key):
|
||||
translatable_fields = get_translatable_fields_for_model(model)
|
||||
if translatable_fields is not None:
|
||||
pieces = lookup_key.split('__')
|
||||
# If we are doing a lookup on a translatable field,
|
||||
# we want to rewrite it to the actual field name
|
||||
# For example, we want to rewrite "name__startswith" to "name_fr__startswith"
|
||||
if pieces[0] in translatable_fields:
|
||||
lookup_key = build_localized_fieldname(pieces[0], get_language())
|
||||
|
||||
remaining_lookup = '__'.join(pieces[1:])
|
||||
if remaining_lookup:
|
||||
lookup_key = '%s__%s' % (lookup_key, remaining_lookup)
|
||||
|
||||
pieces = lookup_key.split('__')
|
||||
if len(pieces) > 1:
|
||||
# Check if we are doing a lookup to a related trans model
|
||||
fields_to_trans_models = get_fields_to_translatable_models(model)
|
||||
for field_to_trans, transmodel in fields_to_trans_models:
|
||||
if pieces[0] == field_to_trans:
|
||||
sub_lookup = '__'.join(pieces[1:])
|
||||
if sub_lookup:
|
||||
sub_lookup = rewrite_lookup_key(transmodel, sub_lookup)
|
||||
lookup_key = '%s__%s' % (pieces[0], sub_lookup)
|
||||
break
|
||||
|
||||
return lookup_key
|
||||
|
||||
|
||||
def get_fields_to_translatable_models(model):
|
||||
results = []
|
||||
for field_name in model._meta.get_all_field_names():
|
||||
field_object, modelclass, direct, m2m = model._meta.get_field_by_name(field_name)
|
||||
if direct and isinstance(field_object, RelatedField):
|
||||
if get_translatable_fields_for_model(field_object.related.parent_model) is not None:
|
||||
results.append((field_name, field_object.related.parent_model))
|
||||
return results
|
||||
|
||||
|
||||
class MultilingualQuerySet(models.query.QuerySet):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(MultilingualQuerySet, self).__init__(*args, **kwargs)
|
||||
if self.model and (not self.query.order_by):
|
||||
if self.model._meta.ordering:
|
||||
# If we have default ordering specified on the model, set it now so that
|
||||
# it can be rewritten. Otherwise sql.compiler will grab it directly from _meta
|
||||
ordering = []
|
||||
for key in self.model._meta.ordering:
|
||||
ordering.append(rewrite_lookup_key(self.model, key))
|
||||
self.query.add_ordering(*ordering)
|
||||
|
||||
# This method was not present in django-linguo
|
||||
def _rewrite_q(self, q):
|
||||
"Rewrite field names inside Q call."
|
||||
if isinstance(q, tuple) and len(q) == 2:
|
||||
return rewrite_lookup_key(self.model, q[0]), q[1]
|
||||
if isinstance(q, Node):
|
||||
q.children = map(self._rewrite_q, q.children)
|
||||
return q
|
||||
|
||||
# This method was not present in django-linguo
|
||||
def _rewrite_f(self, q):
|
||||
"Rewrite field names inside F call."
|
||||
if isinstance(q, models.F):
|
||||
q.name = rewrite_lookup_key(self.model, q.name)
|
||||
return q
|
||||
if isinstance(q, Node):
|
||||
q.children = map(self._rewrite_f, q.children)
|
||||
return q
|
||||
|
||||
def _filter_or_exclude(self, negate, *args, **kwargs):
|
||||
args = map(self._rewrite_q, args)
|
||||
for key, val in kwargs.items():
|
||||
new_key = rewrite_lookup_key(self.model, key)
|
||||
del kwargs[key]
|
||||
kwargs[new_key] = self._rewrite_f(val)
|
||||
return super(MultilingualQuerySet, self)._filter_or_exclude(negate, *args, **kwargs)
|
||||
|
||||
def order_by(self, *field_names):
|
||||
new_args = []
|
||||
for key in field_names:
|
||||
new_args.append(rewrite_lookup_key(self.model, key))
|
||||
return super(MultilingualQuerySet, self).order_by(*new_args)
|
||||
|
||||
def update(self, **kwargs):
|
||||
for key, val in kwargs.items():
|
||||
new_key = rewrite_lookup_key(self.model, key)
|
||||
del kwargs[key]
|
||||
kwargs[new_key] = self._rewrite_f(val)
|
||||
return super(MultilingualQuerySet, self).update(**kwargs)
|
||||
update.alters_data = True
|
||||
|
||||
# This method was not present in django-linguo
|
||||
def create(self, **kwargs):
|
||||
populate = kwargs.pop('_populate', settings.AUTO_POPULATE)
|
||||
if populate:
|
||||
translatable_fields = get_translatable_fields_for_model(self.model)
|
||||
if translatable_fields is not None:
|
||||
for key, val in kwargs.items():
|
||||
if key in translatable_fields:
|
||||
# Try to add value in every language
|
||||
for new_key in translatable_fields[key]:
|
||||
kwargs.setdefault(new_key, val)
|
||||
else:
|
||||
# If not use populate feature, then perform normal rewriting
|
||||
for key, val in kwargs.items():
|
||||
new_key = rewrite_lookup_key(self.model, key)
|
||||
del kwargs[key]
|
||||
kwargs[new_key] = val
|
||||
return super(MultilingualQuerySet, self).create(**kwargs)
|
||||
|
||||
|
||||
class MultilingualManager(models.Manager):
|
||||
use_for_related_fields = True
|
||||
|
||||
def get_query_set(self):
|
||||
return MultilingualQuerySet(self.model)
|
||||
|
|
@ -32,3 +32,6 @@ ENABLE_REGISTRATIONS = getattr(
|
|||
# Modeltranslation specific debug setting
|
||||
DEBUG = getattr(
|
||||
settings, 'MODELTRANSLATION_DEBUG', settings.DEBUG)
|
||||
|
||||
AUTO_POPULATE = getattr(
|
||||
settings, 'MODELTRANSLATION_AUTO_POPULATE', False)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ from django.contrib.admin.sites import AdminSite
|
|||
from django.contrib.auth.models import User
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.files.base import ContentFile
|
||||
from django.db.models import Q, F
|
||||
from django.db.models.loading import AppCache
|
||||
from django.test import TestCase
|
||||
from django.utils.datastructures import SortedDict
|
||||
|
|
@ -28,7 +29,7 @@ from modeltranslation.admin import (TranslationAdmin,
|
|||
from modeltranslation.tests.models import (
|
||||
AbstractModelB, MultitableModelA, DataModel, FallbackModel, FallbackModel2,
|
||||
FileFieldsModel, OtherFieldsModel, TestModel, MultitableBModelA, MultitableModelC,
|
||||
MultitableDTestModel)
|
||||
MultitableDTestModel, ManagerTestModel, CustomManagerTestModel)
|
||||
from modeltranslation.tests.translation import FallbackModel2TranslationOptions
|
||||
from modeltranslation.tests.test_settings import TEST_SETTINGS
|
||||
|
||||
|
|
@ -36,6 +37,10 @@ try:
|
|||
from django.test.utils import override_settings
|
||||
except ImportError:
|
||||
from modeltranslation.tests.utils import override_settings
|
||||
try:
|
||||
from django.utils.translation import override
|
||||
except ImportError:
|
||||
from modeltranslation.tests.utils import override # NOQA
|
||||
|
||||
# None of the following tests really depend on the content of the request,
|
||||
# so we'll just pass in None.
|
||||
|
|
@ -151,7 +156,7 @@ class ModeltranslationTest(ModeltranslationTestBase):
|
|||
self.failUnless(translator.translator)
|
||||
|
||||
# Check that all models are registered for translation
|
||||
self.failUnlessEqual(len(translator.translator._registry), 10)
|
||||
self.failUnlessEqual(len(translator.translator._registry), 12)
|
||||
|
||||
# Try to unregister a model that is not registered
|
||||
self.assertRaises(translator.NotRegistered,
|
||||
|
|
@ -215,7 +220,7 @@ class ModeltranslationTest(ModeltranslationTestBase):
|
|||
|
||||
def test_titleonly(self):
|
||||
title1_de = "title de"
|
||||
n = TestModel.objects.create(title=title1_de)
|
||||
n = TestModel(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
|
||||
|
|
@ -352,7 +357,7 @@ class OtherFieldsTest(ModeltranslationTestBase):
|
|||
self.assertEqual('de', get_language())
|
||||
self.assertEqual(7, inst.int)
|
||||
self.assertEqual(7, inst.int_de)
|
||||
self.assertEqual(42, inst.int_en) # default value is honored
|
||||
self.assertEqual(42, inst.int_en) # default value is honored
|
||||
|
||||
inst.int += 2
|
||||
inst.save()
|
||||
|
|
@ -1121,3 +1126,161 @@ class TranslationAdminTest(ModeltranslationTestBase):
|
|||
ma_fieldsets = ma.inlines[0](
|
||||
TestModel, self.site).get_fieldsets(request, self.test_obj)
|
||||
self.assertEqual(ma_fieldsets, fieldsets)
|
||||
|
||||
|
||||
class TestManager(ModeltranslationTestBase):
|
||||
def setUp(self):
|
||||
# In this test case the default language is en, not de.
|
||||
trans_real.activate('en')
|
||||
|
||||
def test_filter_update(self):
|
||||
"""Test if filtering and updating is language-aware."""
|
||||
n = ManagerTestModel(title='')
|
||||
n.title_en = 'en'
|
||||
n.title_de = 'de'
|
||||
n.save()
|
||||
|
||||
m = ManagerTestModel(title='')
|
||||
m.title_en = 'title en'
|
||||
m.title_de = 'de'
|
||||
m.save()
|
||||
|
||||
self.assertEqual('en', get_language())
|
||||
|
||||
self.assertEqual(0, ManagerTestModel.objects.filter(title='de').count())
|
||||
self.assertEqual(1, ManagerTestModel.objects.filter(title='en').count())
|
||||
# Spanning works
|
||||
self.assertEqual(2, ManagerTestModel.objects.filter(title__contains='en').count())
|
||||
|
||||
with override('de'):
|
||||
self.assertEqual(2, ManagerTestModel.objects.filter(title='de').count())
|
||||
self.assertEqual(0, ManagerTestModel.objects.filter(title='en').count())
|
||||
# Spanning works
|
||||
self.assertEqual(2, ManagerTestModel.objects.filter(title__endswith='e').count())
|
||||
|
||||
# Still possible to use explicit language version
|
||||
self.assertEqual(1, ManagerTestModel.objects.filter(title_en='en').count())
|
||||
self.assertEqual(2, ManagerTestModel.objects.filter(title_en__contains='en').count())
|
||||
|
||||
ManagerTestModel.objects.update(title='new')
|
||||
self.assertEqual(2, ManagerTestModel.objects.filter(title='new').count())
|
||||
n = ManagerTestModel.objects.get(pk=n.pk)
|
||||
m = ManagerTestModel.objects.get(pk=m.pk)
|
||||
self.assertEqual('en', n.title_en)
|
||||
self.assertEqual('new', n.title_de)
|
||||
self.assertEqual('title en', m.title_en)
|
||||
self.assertEqual('new', m.title_de)
|
||||
|
||||
def test_q(self):
|
||||
"""Test if Q queries are rewritten."""
|
||||
n = ManagerTestModel(title='')
|
||||
n.title_en = 'en'
|
||||
n.title_de = 'de'
|
||||
n.save()
|
||||
|
||||
self.assertEqual('en', get_language())
|
||||
self.assertEqual(0, ManagerTestModel.objects.filter(Q(title='de') | Q(pk=42)).count())
|
||||
self.assertEqual(1, ManagerTestModel.objects.filter(Q(title='en') | Q(pk=42)).count())
|
||||
|
||||
with override('de'):
|
||||
self.assertEqual(1, ManagerTestModel.objects.filter(Q(title='de') | Q(pk=42)).count())
|
||||
self.assertEqual(0, ManagerTestModel.objects.filter(Q(title='en') | Q(pk=42)).count())
|
||||
|
||||
def test_f(self):
|
||||
"""Test if F queries are rewritten."""
|
||||
n = ManagerTestModel.objects.create(visits_en=1, visits_de=2)
|
||||
|
||||
self.assertEqual('en', get_language())
|
||||
ManagerTestModel.objects.update(visits=F('visits') + 10)
|
||||
n = ManagerTestModel.objects.all()[0]
|
||||
self.assertEqual(n.visits_en, 11)
|
||||
self.assertEqual(n.visits_de, 2)
|
||||
|
||||
with override('de'):
|
||||
ManagerTestModel.objects.update(visits=F('visits') + 20)
|
||||
n = ManagerTestModel.objects.all()[0]
|
||||
self.assertEqual(n.visits_en, 11)
|
||||
self.assertEqual(n.visits_de, 22)
|
||||
|
||||
def test_custom_manager(self):
|
||||
"""Test if user-defined manager is still working"""
|
||||
n = CustomManagerTestModel(title='')
|
||||
n.title_en = 'enigma'
|
||||
n.title_de = 'foo'
|
||||
n.save()
|
||||
|
||||
m = CustomManagerTestModel(title='')
|
||||
m.title_en = 'enigma'
|
||||
m.title_de = 'bar'
|
||||
m.save()
|
||||
|
||||
# Custom method
|
||||
self.assertEqual('bar', CustomManagerTestModel.objects.foo())
|
||||
|
||||
# Ensure that get_query_set is working - filter objects to those with 'a' in title
|
||||
self.assertEqual('en', get_language())
|
||||
self.assertEqual(2, CustomManagerTestModel.objects.count())
|
||||
with override('de'):
|
||||
self.assertEqual(1, CustomManagerTestModel.objects.count())
|
||||
|
||||
def test_creation(self):
|
||||
"""Test if field are rewritten in create."""
|
||||
self.assertEqual('en', get_language())
|
||||
n = ManagerTestModel.objects.create(title='foo')
|
||||
self.assertEqual('foo', n.title_en)
|
||||
self.assertEqual(None, n.title_de)
|
||||
self.assertEqual('foo', n.title)
|
||||
|
||||
# The same result
|
||||
n = ManagerTestModel.objects.create(title_en='foo')
|
||||
self.assertEqual('foo', n.title_en)
|
||||
self.assertEqual(None, n.title_de)
|
||||
self.assertEqual('foo', n.title)
|
||||
|
||||
def test_creation_population(self):
|
||||
"""Test if language fields are populated with default value on creation."""
|
||||
n = ManagerTestModel.objects.create(title='foo', _populate=True)
|
||||
self.assertEqual('foo', n.title_en)
|
||||
self.assertEqual('foo', n.title_de)
|
||||
self.assertEqual('foo', n.title)
|
||||
|
||||
# You can specify some language...
|
||||
n = ManagerTestModel.objects.create(title='foo', title_de='bar', _populate=True)
|
||||
self.assertEqual('foo', n.title_en)
|
||||
self.assertEqual('bar', n.title_de)
|
||||
self.assertEqual('foo', n.title)
|
||||
|
||||
# ... but remember that still original attribute points to current language
|
||||
self.assertEqual('en', get_language())
|
||||
n = ManagerTestModel.objects.create(title='foo', title_en='bar', _populate=True)
|
||||
self.assertEqual('bar', n.title_en)
|
||||
self.assertEqual('foo', n.title_de)
|
||||
self.assertEqual('bar', n.title) # points to en
|
||||
with override('de'):
|
||||
self.assertEqual('foo', n.title) # points to de
|
||||
self.assertEqual('en', get_language())
|
||||
|
||||
# This feature (for backward-compatibility) require _populate keyword...
|
||||
n = ManagerTestModel.objects.create(title='foo')
|
||||
self.assertEqual('foo', n.title_en)
|
||||
self.assertEqual(None, n.title_de)
|
||||
self.assertEqual('foo', n.title)
|
||||
|
||||
# ... or MODELTRANSLATION_AUTO_POPULATE setting
|
||||
with override_settings(MODELTRANSLATION_AUTO_POPULATE=True):
|
||||
reload(mt_settings)
|
||||
self.assertEqual(True, mt_settings.AUTO_POPULATE)
|
||||
n = ManagerTestModel.objects.create(title='foo')
|
||||
self.assertEqual('foo', n.title_en)
|
||||
self.assertEqual('foo', n.title_de)
|
||||
self.assertEqual('foo', n.title)
|
||||
|
||||
# _populate keyword has highest priority
|
||||
n = ManagerTestModel.objects.create(title='foo', _populate=False)
|
||||
self.assertEqual('foo', n.title_en)
|
||||
self.assertEqual(None, n.title_de)
|
||||
self.assertEqual('foo', n.title)
|
||||
|
||||
# Restore previous state
|
||||
reload(mt_settings)
|
||||
self.assertEqual(False, mt_settings.AUTO_POPULATE)
|
||||
|
|
|
|||
|
|
@ -69,3 +69,21 @@ class AbstractModelB(AbstractModelA):
|
|||
|
||||
class DataModel(models.Model):
|
||||
data = models.TextField(blank=True, null=True)
|
||||
|
||||
|
||||
class ManagerTestModel(models.Model):
|
||||
title = models.CharField(ugettext_lazy('title'), max_length=255)
|
||||
visits = models.IntegerField(ugettext_lazy('visits'), default=0)
|
||||
|
||||
|
||||
class CustomManager(models.Manager):
|
||||
def get_query_set(self):
|
||||
return super(CustomManager, self).get_query_set().filter(title__contains='a')
|
||||
|
||||
def foo(self):
|
||||
return 'bar'
|
||||
|
||||
|
||||
class CustomManagerTestModel(models.Model):
|
||||
title = models.CharField(ugettext_lazy('title'), max_length=255)
|
||||
objects = CustomManager()
|
||||
|
|
|
|||
|
|
@ -26,3 +26,5 @@ LANGUAGE_CODE = 'de'
|
|||
DEFAULT_LANGUAGE = 'de'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
MODELTRANSLATION_AUTO_POPULATE = False
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ from modeltranslation.translator import translator, TranslationOptions
|
|||
from modeltranslation.tests.models import (
|
||||
TestModel, FallbackModel, FallbackModel2,
|
||||
FileFieldsModel, OtherFieldsModel, AbstractModelA, AbstractModelB,
|
||||
MultitableModelA, MultitableBModelA, MultitableModelC)
|
||||
MultitableModelA, MultitableBModelA, MultitableModelC,
|
||||
ManagerTestModel, CustomManagerTestModel)
|
||||
|
||||
|
||||
class TestTranslationOptions(TranslationOptions):
|
||||
|
|
@ -59,3 +60,13 @@ translator.register(AbstractModelA, AbstractModelATranslationOptions)
|
|||
class AbstractModelBTranslationOptions(TranslationOptions):
|
||||
fields = ('titleb',)
|
||||
translator.register(AbstractModelB, AbstractModelBTranslationOptions)
|
||||
|
||||
|
||||
class ManagerTestModelTranslationOptions(TranslationOptions):
|
||||
fields = ('title', 'visits')
|
||||
translator.register(ManagerTestModel, ManagerTestModelTranslationOptions)
|
||||
|
||||
|
||||
class CustomManagerTestModelTranslationOptions(TranslationOptions):
|
||||
fields = ('title',)
|
||||
translator.register(CustomManagerTestModel, CustomManagerTestModelTranslationOptions)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
"""
|
||||
This is Django 1.4 override_settings decorator backported for compatibility with Django 1.3.
|
||||
This are Django 1.4 override_settings decorator and override (language) context manager
|
||||
backported for compatibility with Django 1.3.
|
||||
|
||||
The only difference is that this version does not use settings_changes signal
|
||||
(because there is no such signal).
|
||||
|
|
@ -8,6 +9,7 @@ from __future__ import with_statement # Python 2.5 compatibility
|
|||
|
||||
from django.utils.functional import wraps
|
||||
from django.conf import settings, UserSettingsHolder
|
||||
from django.utils.translation import get_language, activate, deactivate, deactivate_all
|
||||
|
||||
|
||||
class override_settings(object):
|
||||
|
|
@ -58,3 +60,22 @@ class override_settings(object):
|
|||
|
||||
def disable(self):
|
||||
settings._wrapped = self.wrapped
|
||||
|
||||
|
||||
class override(object):
|
||||
def __init__(self, language, deactivate=False):
|
||||
self.language = language
|
||||
self.deactivate = deactivate
|
||||
self.old_language = get_language()
|
||||
|
||||
def __enter__(self):
|
||||
if self.language is not None:
|
||||
activate(self.language)
|
||||
else:
|
||||
deactivate_all()
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
if self.deactivate:
|
||||
deactivate()
|
||||
else:
|
||||
activate(self.old_language)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from django.conf import settings
|
||||
from django.db.models import Manager
|
||||
from django.db.models.base import ModelBase
|
||||
|
||||
from modeltranslation.fields import (TranslationFieldDescriptor,
|
||||
create_translation_field)
|
||||
from modeltranslation.manager import MultilingualManager
|
||||
from modeltranslation.utils import build_localized_fieldname
|
||||
|
||||
|
||||
|
|
@ -31,7 +33,7 @@ class TranslationOptions(object):
|
|||
|
||||
def add_localized_fields(model):
|
||||
"""
|
||||
Monkey patchs the original model class to provide additional fields for
|
||||
Monkey patches 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.
|
||||
|
||||
|
|
@ -61,6 +63,26 @@ def add_localized_fields(model):
|
|||
return localized_fields
|
||||
|
||||
|
||||
def add_manager(model):
|
||||
"""
|
||||
Monkey patches the original model to use MultilingualManager instead of
|
||||
default manager (``objects``).
|
||||
|
||||
If model has a custom manager, then merge it with MultilingualManager.
|
||||
"""
|
||||
if not hasattr(model, 'objects'):
|
||||
return
|
||||
current_manager = model.objects
|
||||
if isinstance(current_manager, MultilingualManager):
|
||||
return
|
||||
if current_manager.__class__ is Manager:
|
||||
current_manager.__class__ = MultilingualManager
|
||||
else:
|
||||
class NewMultilingualManager(current_manager.__class__, MultilingualManager):
|
||||
pass
|
||||
current_manager.__class__ = NewMultilingualManager
|
||||
|
||||
|
||||
#def translated_model_initialized(field_names, instance, **kwargs):
|
||||
#print "translated_model_initialized instance:", \
|
||||
#instance, ", field:", field_names
|
||||
|
|
@ -152,6 +174,10 @@ class Translator(object):
|
|||
for related_obj in model._meta.get_all_related_objects():
|
||||
delete_cache_fields(related_obj.model)
|
||||
|
||||
# Set MultilingualManager
|
||||
add_manager(model)
|
||||
|
||||
# Substitute original field with descriptor
|
||||
model_fallback_values = getattr(
|
||||
translation_opts, 'fallback_values', None)
|
||||
for field_name in translation_opts.fields:
|
||||
|
|
|
|||
Loading…
Reference in a new issue