# -*- coding: utf-8 -*- """ TODO: Merge autoregister tests from django-modeltranslation-wrapper. NOTE: Perhaps ModeltranslationTestBase in tearDownClass should reload some modules, so that tests for other apps are in the same environment. """ from __future__ import with_statement # Python 2.5 compatibility import os import shutil from django import forms from django.conf import settings as django_settings from django.contrib.admin.sites import AdminSite from django.contrib.auth.models import User from django.core.exceptions import ValidationError, ImproperlyConfigured 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 from django.utils.translation import get_language, trans_real from modeltranslation import settings as mt_settings from modeltranslation import translator from modeltranslation.admin import (TranslationAdmin, TranslationStackedInline) from modeltranslation.tests.models import ( AbstractModelB, MultitableModelA, DataModel, FallbackModel, FallbackModel2, FileFieldsModel, OtherFieldsModel, TestModel, MultitableBModelA, MultitableModelC, MultitableDTestModel, ManagerTestModel, CustomManagerTestModel) from modeltranslation.tests.translation import FallbackModel2TranslationOptions from modeltranslation.tests.test_settings import TEST_SETTINGS 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. request = None class ModeltranslationTestBase(TestCase): urls = 'modeltranslation.tests.urls' cache = AppCache() synced = False @classmethod def setUpClass(cls): """ Prepare database: * Call syncdb to create tables for tests.models (since during default testrunner's db creation modeltranslation.tests was not in INSTALLED_APPS """ super(ModeltranslationTestBase, cls).setUpClass() if not ModeltranslationTestBase.synced: # In odred to perform only one syncdb ModeltranslationTestBase.synced = True with override_settings(**TEST_SETTINGS): import sys # 1. Reload translation in case USE_I18N was False from django.utils import translation reload(translation) # 2. Reload MT because LANGUAGES likely changed. reload(mt_settings) reload(translator) from modeltranslation import admin reload(admin) # 3. Reset test models (because autodiscover have already run, those models # have translation fields, but for languages previously defined. We want # to be sure that 'de' and 'en' are available) del cls.cache.app_models['tests'] from modeltranslation.tests import models reload(models) cls.cache.load_app('modeltranslation.tests') sys.modules.pop('modeltranslation.tests.translation', None) # 4. Autodiscover from modeltranslation import models reload(models) # 5. Reload some imported classes cls.reload_globals('modeltranslation.tests.models') cls.reload_globals('modeltranslation.admin') # 6. Syncdb (``migrate=False`` in case of south) from django.db import connections, DEFAULT_DB_ALIAS from django.core.management import call_command call_command('syncdb', verbosity=0, migrate=False, interactive=False, database=connections[DEFAULT_DB_ALIAS].alias, load_initial_data=False) @staticmethod def reload_globals(module): """ Very ugly method for reloading things imported from module. It wouldn't be needed if eg. ``TestModel`` calls would be replaced by ``models.TestModel``. """ names = [] for name, item in globals().items(): if hasattr(item, '__module__') and item.__module__ == module: names.append(name) _temp = __import__(module, globals(), locals(), names, -1) for name in names: globals()[name] = getattr(_temp, name) @classmethod def clear_cache(cls): """ It is necessary to clear cache - otherwise model reloading won't recreate models, but just use old ones. """ cls.cache.app_store = SortedDict() cls.cache.app_models = SortedDict() cls.cache.app_errors = {} cls.cache.handled = {} cls.cache.loaded = False @classmethod def reset_cache(cls): """ Rebuild whole cache, import all models again """ cls.clear_cache() cls.cache._populate() for m in cls.cache.get_apps(): reload(m) def setUp(self): trans_real.activate('de') def tearDown(self): trans_real.deactivate() ModeltranslationTestBase = override_settings(**TEST_SETTINGS)(ModeltranslationTestBase) class ModeltranslationTest(ModeltranslationTestBase): """Basic tests for the modeltranslation application.""" def test_registration(self): langs = tuple(l[0] for l in django_settings.LANGUAGES) self.failUnlessEqual(2, len(langs)) self.failUnless('de' in langs) self.failUnless('en' in langs) self.failUnless(translator.translator) # Check that all models are registered for translation self.failUnlessEqual(len(translator.translator._registry), 12) # 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): # 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('title_de' in field_names) self.failUnless('title_en' in field_names) self.failUnless('text' in field_names) self.failUnless('text_de' in field_names) self.failUnless('text_en' in field_names) self.failUnless('url' in field_names) self.failUnless('url_de' in field_names) self.failUnless('url_en' in field_names) self.failUnless('email' in field_names) self.failUnless('email_de' in field_names) self.failUnless('email_en' in field_names) inst.delete() def test_verbose_name(self): inst = TestModel.objects.create(title="Testtitle", text="Testtext") self.assertEquals(unicode( inst._meta.get_field('title_de').verbose_name), u'title [de]') inst.delete() def test_set_translation(self): self.failUnlessEqual(get_language(), 'de') # First create an instance of the test model to play with title1_de = "title de" title1_en = "title en" title2_de = "title2 de" inst1 = TestModel(title_en=title1_en, text="Testtext") inst1.title = title1_de 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) 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(title=title1_de) # The original field "title" passed in the constructor is also # populated for the current language field: "title_de". self.failUnlessEqual(n.title, title1_de) self.failUnlessEqual(n.title_de, title1_de) self.failUnlessEqual(n.title_en, None) def test_fallback_values_1(self): """ If ``fallback_values`` is set to string, all untranslated fields would return this string. """ title1_de = "title de" n = FallbackModel() n.title = title1_de n.save() del n n = FallbackModel.objects.get(title=title1_de) self.failUnlessEqual(n.title, title1_de) trans_real.activate("en") self.failUnlessEqual(n.title, "") def test_fallback_values_2(self): """ If ``fallback_values`` is set to ``dict``, all untranslated fields in ``dict`` would return this mapped value. Fields not in ``dict`` would return default translation. """ title1_de = "title de" text1_de = "text in german" n = FallbackModel2() n.title = title1_de n.text = text1_de n.save() del n n = FallbackModel2.objects.get(title=title1_de) trans_real.activate("en") self.failUnlessEqual(n.title, '') # Falling back to default field value self.failUnlessEqual( n.text, FallbackModel2TranslationOptions.fallback_values['text']) def _compare_instances(self, x, y, field): self.assertEqual(getattr(x, field), getattr(y, field), "Constructor diff on field %s." % field) def _test_constructor(self, keywords): n = TestModel(**keywords) m = TestModel.objects.create(**keywords) fields = translator.translator.get_options_for_model(TestModel).localized_fieldnames for base_field, trans_fields in fields.iteritems(): self._compare_instances(n, m, base_field) for lang_field in trans_fields: self._compare_instances(n, m, lang_field) def test_constructor(self): """ Ensure that model constructor behaves exactly the same as objects.create """ # test different arguments compositions keywords = dict( # original only title='title', # both languages + original email='q@q.qq', email_de='d@d.dd', email_en='e@e.ee', # both languages without original text_en='text en', text_de='text de', ) self._test_constructor(keywords) keywords = dict( # only current language title_de='title', # only not current language url_en='http://www.google.com', # original + current text='text def', text_de='text de', # original + not current email='q@q.qq', email_en='e@e.ee', ) self._test_constructor(keywords) class FallbackTests(ModeltranslationTestBase): test_fallback = { 'default': ('de',), 'de': ('en',) } def tearDown(self): trans_real.deactivate() reload(mt_settings) # Return to previous state def test_settings(self): # Initial self.assertEqual(mt_settings.FALLBACK_LANGUAGES, {'default': ()}) # Tuple/list with override_settings(MODELTRANSLATION_FALLBACK_LANGUAGES=('de',)): reload(mt_settings) self.assertEqual(mt_settings.FALLBACK_LANGUAGES, {'default': ('de',)}) # Whole dict with override_settings(MODELTRANSLATION_FALLBACK_LANGUAGES=self.test_fallback): reload(mt_settings) self.assertEqual(mt_settings.FALLBACK_LANGUAGES, self.test_fallback) # Improper language raises error config = {'default': (), 'fr': ('en',)} with override_settings(MODELTRANSLATION_FALLBACK_LANGUAGES=config): self.assertRaises(ImproperlyConfigured, lambda: reload(mt_settings)) def test_resolution_order(self): from modeltranslation.utils import resolution_order with override_settings(MODELTRANSLATION_FALLBACK_LANGUAGES=self.test_fallback): reload(mt_settings) self.assertEqual(('en', 'de'), resolution_order('en')) self.assertEqual(('de', 'en'), resolution_order('de')) # Overriding config = {'default': ()} self.assertEqual(('en',), resolution_order('en', config)) self.assertEqual(('de', 'en'), resolution_order('de', config)) # Uniqueness config = {'de': ('en', 'de')} self.assertEqual(('en', 'de'), resolution_order('en', config)) self.assertEqual(('de', 'en'), resolution_order('de', config)) # Default fallbacks are always used at the end # That's it: fallbacks specified for a language don't replace defaults, # but just are prepended config = {'default': ('en', 'de'), 'de': ()} self.assertEqual(('en', 'de'), resolution_order('en', config)) self.assertEqual(('de', 'en'), resolution_order('de', config)) # What one may have expected self.assertNotEqual(('de',), resolution_order('de', config)) # To completely override settings, one should override all keys config = {'default': (), 'de': ()} self.assertEqual(('en',), resolution_order('en', config)) self.assertEqual(('de',), resolution_order('de', config)) def test_fallback_languages(self): with override_settings(MODELTRANSLATION_FALLBACK_LANGUAGES=self.test_fallback): reload(mt_settings) title_de = 'title de' title_en = 'title en' n = TestModel(title=title_de) self.assertEqual(n.title_de, title_de) self.assertEqual(n.title_en, None) self.assertEqual(n.title, title_de) trans_real.activate('en') self.assertEqual(n.title, title_de) # since default fallback is de n = TestModel(title=title_en) self.assertEqual(n.title_de, None) self.assertEqual(n.title_en, title_en) self.assertEqual(n.title, title_en) trans_real.activate('de') self.assertEqual(n.title, title_en) # since fallback for de is en n.title_en = None self.assertEqual(n.title, '') # if all fallbacks fail, return field.get_default() class FileFieldsTest(ModeltranslationTestBase): test_media_root = os.path.join( os.path.dirname(os.path.realpath(__file__)), 'media') def tearDown(self): # File tests create a temporary media directory structure. While the # files are automatically deleted by the storage, the directories will # stay. So we clean up a bit... if os.path.isdir(self.test_media_root): shutil.rmtree(self.test_media_root) trans_real.deactivate() def test_translated_models(self): # First create an instance of the test model to play with inst = FileFieldsModel.objects.create( title="Testtitle", file=None) field_names = dir(inst) self.failUnless('id' in field_names) self.failUnless('title' in field_names) self.failUnless('title_de' in field_names) self.failUnless('title_en' in field_names) self.failUnless('file' in field_names) self.failUnless('file_de' in field_names) self.failUnless('file_en' in field_names) inst.delete() def test_translated_models_instance(self): #f_en = ContentFile("Just a really good file") inst = FileFieldsModel(title="Testtitle", file=None) trans_real.activate("en") inst.title = 'title_en' inst.file = 'a_en' inst.file.save('b_en', ContentFile('file in english')) inst.image = 'i_en.jpg' inst.image.save('i_en.jpg', ContentFile('image in english')) trans_real.activate("de") inst.title = 'title_de' inst.file = 'a_de' inst.file.save('b_de', ContentFile('file in german')) inst.image = 'i_de.jpg' inst.image.save('i_de.jpg', ContentFile('image in germany')) inst.save() trans_real.activate("en") self.failUnlessEqual(inst.title, 'title_en') self.failUnless(inst.file.name.count('b_en') > 0) self.failUnless(inst.image.name.count('i_en') > 0) trans_real.activate("de") self.failUnlessEqual(inst.title, 'title_de') self.failUnless(inst.file.name.count('b_de') > 0) self.failUnless(inst.image.name.count('i_de') > 0) inst.file_en.delete() inst.image_en.delete() inst.file_de.delete() inst.image_de.delete() inst.delete() class OtherFieldsTest(ModeltranslationTestBase): def test_translated_models(self): inst = OtherFieldsModel.objects.create() field_names = dir(inst) self.failUnless('id' in field_names) self.failUnless('int' in field_names) self.failUnless('int_de' in field_names) self.failUnless('int_en' in field_names) self.failUnless('boolean' in field_names) self.failUnless('boolean_de' in field_names) self.failUnless('boolean_en' in field_names) self.failUnless('nullboolean' in field_names) self.failUnless('nullboolean_de' in field_names) self.failUnless('nullboolean_en' in field_names) self.failUnless('csi' in field_names) self.failUnless('csi_de' in field_names) self.failUnless('csi_en' in field_names) inst.delete() def test_translated_models_integer_instance(self): inst = OtherFieldsModel() inst.int = 7 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 inst.int += 2 inst.save() self.assertEqual(9, inst.int) self.assertEqual(9, inst.int_de) self.assertEqual(42, inst.int_en) trans_real.activate('en') inst.int -= 1 self.assertEqual(41, inst.int) self.assertEqual(9, inst.int_de) self.assertEqual(41, inst.int_en) # this field has validator - let's try to make it below 0! inst.int -= 50 self.assertRaises(ValidationError, inst.full_clean) def test_translated_models_boolean_instance(self): inst = OtherFieldsModel() inst.boolean = True self.assertEqual('de', get_language()) self.assertEqual(True, inst.boolean) self.assertEqual(True, inst.boolean_de) self.assertEqual(False, inst.boolean_en) inst.boolean = False inst.save() self.assertEqual(False, inst.boolean) self.assertEqual(False, inst.boolean_de) self.assertEqual(False, inst.boolean_en) trans_real.activate('en') inst.boolean = True self.assertEqual(True, inst.boolean) self.assertEqual(False, inst.boolean_de) self.assertEqual(True, inst.boolean_en) def test_translated_models_nullboolean_instance(self): inst = OtherFieldsModel() inst.nullboolean = True self.assertEqual('de', get_language()) self.assertEqual(True, inst.nullboolean) self.assertEqual(True, inst.nullboolean_de) self.assertEqual(None, inst.nullboolean_en) inst.nullboolean = False inst.save() self.assertEqual(False, inst.nullboolean) self.assertEqual(False, inst.nullboolean_de) self.assertEqual(None, inst.nullboolean_en) trans_real.activate('en') inst.nullboolean = True self.assertEqual(True, inst.nullboolean) self.assertEqual(False, inst.nullboolean_de) self.assertEqual(True, inst.nullboolean_en) def test_translated_models_commaseparatedinteger_instance(self): inst = OtherFieldsModel() inst.csi = '4,8,15,16,23,42' self.assertEqual('de', get_language()) self.assertEqual('4,8,15,16,23,42', inst.csi) self.assertEqual('4,8,15,16,23,42', inst.csi_de) self.assertEqual(None, inst.csi_en) inst.csi = '23,42' inst.save() self.assertEqual('23,42', inst.csi) self.assertEqual('23,42', inst.csi_de) self.assertEqual(None, inst.csi_en) trans_real.activate('en') inst.csi = '4,8,15,16,23,42' self.assertEqual('4,8,15,16,23,42', inst.csi) self.assertEqual('23,42', inst.csi_de) self.assertEqual('4,8,15,16,23,42', inst.csi_en) # Now that we have covered csi, lost, illuminati and hitchhiker # compliance in a single test, do something useful... # Check if validation is preserved inst.csi = '1;2' self.assertRaises(ValidationError, inst.full_clean) class ModeltranslationTestRule1(ModeltranslationTestBase): """ Rule 1: Reading the value from the original field returns the value in translated to the current language. """ def _test_field(self, field_name, value_de, value_en, deactivate=True): field_name_de = '%s_de' % field_name field_name_en = '%s_en' % field_name params = {'title_de': 'title de', 'title_en': 'title en', field_name_de: value_de, field_name_en: value_en} n = TestModel.objects.create(**params) # Language is set to 'de' at this point self.failUnlessEqual(get_language(), 'de') self.failUnlessEqual(getattr(n, field_name), value_de) self.failUnlessEqual(getattr(n, field_name_de), value_de) self.failUnlessEqual(getattr(n, field_name_en), value_en) # Now switch to "en" trans_real.activate("en") self.failUnlessEqual(get_language(), "en") # Should now be return the english one (just by switching the language) self.failUnlessEqual(getattr(n, field_name), value_en) n = TestModel.objects.create(**params) n.save() # Language is set to "en" at this point self.failUnlessEqual(getattr(n, field_name), value_en) self.failUnlessEqual(getattr(n, field_name_de), value_de) self.failUnlessEqual(getattr(n, field_name_en), value_en) trans_real.activate('de') self.failUnlessEqual(get_language(), 'de') self.failUnlessEqual(getattr(n, field_name), value_de) if deactivate: trans_real.deactivate() def test_rule1(self): """ Basic CharField/TextField test. Could as well call _test_field, just kept for reference. """ title1_de = "title de" title1_en = "title en" text_de = "Dies ist ein deutscher Satz" text_en = "This is an english sentence" 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) self.failUnlessEqual(n.title_de, title1_de) self.failUnlessEqual(n.title_en, title1_en) self.failUnlessEqual(n.text, text_de) self.failUnlessEqual(n.text_de, text_de) self.failUnlessEqual(n.text_en, text_en) # Now switch to "en" trans_real.activate("en") 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) 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 "en" at this point self.failUnlessEqual(n.title, title1_en) self.failUnlessEqual(n.title_de, title1_de) self.failUnlessEqual(n.title_en, title1_en) self.failUnlessEqual(n.text, text_en) self.failUnlessEqual(n.text_de, text_de) self.failUnlessEqual(n.text_en, text_en) trans_real.activate('de') self.failUnlessEqual(get_language(), 'de') self.failUnlessEqual(n.title, title1_de) self.failUnlessEqual(n.text, text_de) trans_real.deactivate() def test_rule1_url_field(self): self._test_field(field_name='url', value_de='http://www.google.de', value_en='http://www.google.com') def test_rule1_email_field(self): self._test_field(field_name='email', value_de='django-modeltranslation@googlecode.de', value_en='django-modeltranslation@googlecode.com') class ModeltranslationTestRule2(ModeltranslationTestBase): """ Rule 2: Assigning a value to the original field also updates the value in the associated translation field of the default language """ def _test_field(self, field_name, value1_de, value1_en, value2, value3, deactivate=True): field_name_de = '%s_de' % field_name field_name_en = '%s_en' % field_name params = {'title_de': 'title de', 'title_en': 'title en', field_name_de: value1_de, field_name_en: value1_en} self.failUnlessEqual(get_language(), 'de') n = TestModel.objects.create(**params) self.failUnlessEqual(getattr(n, field_name), value1_de) self.failUnlessEqual(getattr(n, field_name_de), value1_de) self.failUnlessEqual(getattr(n, field_name_en), value1_en) setattr(n, field_name, value2) n.save() self.failUnlessEqual(getattr(n, field_name), value2) self.failUnlessEqual(getattr(n, field_name), getattr(n, field_name_de)) trans_real.activate("en") self.failUnlessEqual(get_language(), "en") setattr(n, field_name, value3) setattr(n, field_name_de, value1_de) n.save() self.failUnlessEqual(getattr(n, field_name), value3) self.failUnlessEqual(getattr(n, field_name), getattr(n, field_name_en)) self.failUnlessEqual(value1_de, getattr(n, field_name_de)) if deactivate: trans_real.deactivate() def test_rule2(self): """ Basic CharField/TextField test. Could as well call _test_field, just kept for reference. """ 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) self.failUnlessEqual(n.title, title1_de) self.failUnlessEqual(n.title_de, title1_de) self.failUnlessEqual(n.title_en, title1_en) 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() def test_rule2_url_field(self): self._test_field(field_name='url', value1_de='http://www.google.de', value1_en='http://www.google.com', value2='http://www.google.at', value3='http://www.google.co.uk') def test_rule2_email_field(self): self._test_field(field_name='email', value1_de='django-modeltranslation@googlecode.de', value1_en='django-modeltranslation@googlecode.com', value2='django-modeltranslation@googlecode.at', value3='django-modeltranslation@googlecode.co.uk') class ModeltranslationTestRule3(ModeltranslationTestBase): """ 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. """ def _test_field(self, field_name, value1_de, value1_en, value2, value3, deactivate=True): field_name_de = '%s_de' % field_name field_name_en = '%s_en' % field_name params = {'title_de': 'title de', 'title_en': 'title en', field_name_de: value1_de, field_name_en: value1_en} n = TestModel.objects.create(**params) self.failUnlessEqual(get_language(), 'de') self.failUnlessEqual(getattr(n, field_name), value1_de) self.failUnlessEqual(getattr(n, field_name_de), value1_de) self.failUnlessEqual(getattr(n, field_name_en), value1_en) setattr(n, field_name, value2) n.save() self.failUnlessEqual(getattr(n, field_name), getattr(n, field_name_de)) # Now switch to "en" trans_real.activate("en") self.failUnlessEqual(get_language(), "en") setattr(n, field_name_en, value3) # the n.title field is not updated before the instance is saved n.save() self.failUnlessEqual(getattr(n, field_name), getattr(n, field_name_en)) if deactivate: trans_real.deactivate() def test_rule3(self): """ Basic CharField/TextField test. Could as well call _test_field, just kept for reference. """ title1_de = "title de" title1_en = "title en" n = TestModel.objects.create(title_de=title1_de, title_en=title1_en) self.failUnlessEqual(get_language(), 'de') self.failUnlessEqual(mt_settings.DEFAULT_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() # We expect that the original field holds the same value as the german # one (german is the default language). self.failUnlessEqual(n.title, n.title_de) # Fetch the updated object and verify all fields updated_obj = TestModel.objects.get(id=n.id) self.failUnlessEqual(updated_obj.title, 'Neuer Titel') self.failUnlessEqual(updated_obj.title_de, 'Neuer Titel') self.failUnlessEqual(updated_obj.title_en, 'title en') # Now switch to "en" trans_real.activate("en") self.failUnlessEqual(get_language(), "en") n.title_en = "New title" # the n.title field is not updated before the instance is saved n.save() # We expect that the original field has *not* been changed as german # is the default language and we only changed the value of the english # field. # FIXME: Demonstrates a wrong behaviour of save when the current # language is different than the default language. In this case the # original field is set to value of the current language's field. # See issue 33 for details. # TODO: Reactivate, temporarily deactivated for a full run of travis ci #self.failUnlessEqual(n.title, n.title_de) # Fetch the updated object and verify all fields #updated_obj = TestModel.objects.get(id=n.id) #self.failUnlessEqual(updated_obj.title, 'Neuer Titel') #self.failUnlessEqual(updated_obj.title_de, 'Neuer Titel') #self.failUnlessEqual(updated_obj.title_en, 'New title') trans_real.deactivate() def test_rule3_url_field(self): self._test_field(field_name='url', value1_de='http://www.google.de', value1_en='http://www.google.com', value2='http://www.google.at', value3='http://www.google.co.uk') def test_rule3_email_field(self): self._test_field(field_name='email', value1_de='django-modeltranslation@googlecode.de', value1_en='django-modeltranslation@googlecode.com', value2='django-modeltranslation@googlecode.at', value3='django-modeltranslation@googlecode.co.uk') class ModeltranslationTestRule4(ModeltranslationTestBase): """ 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. """ def _test_field(self, field_name, value1_de, value1_en, value2_de, value2_en, value3, deactivate=True): field_name_de = '%s_de' % field_name field_name_en = '%s_en' % field_name params = {'title_de': 'title de', 'title_en': 'title en', field_name_de: value1_de, field_name_en: value1_en} n = TestModel.objects.create(**params) self.failUnlessEqual(getattr(n, field_name), value1_de) self.failUnlessEqual(getattr(n, field_name_de), value1_de) self.failUnlessEqual(getattr(n, field_name_en), value1_en) setattr(n, field_name, value3) setattr(n, field_name_de, value2_de) setattr(n, field_name_en, value2_en) n.save() self.failUnlessEqual(getattr(n, field_name), value2_de) self.failUnlessEqual(getattr(n, field_name_de), value2_de) self.failUnlessEqual(getattr(n, field_name_en), value2_en) setattr(n, field_name, value3) n.save() self.failUnlessEqual(getattr(n, field_name), value3) self.failUnlessEqual(getattr(n, field_name_de), value3) self.failUnlessEqual(getattr(n, field_name_en), value2_en) if deactivate: trans_real.deactivate() def test_rule4(self): """ Basic CharField/TextField test. Could as well call _test_field, just kept for reference. """ 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) 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" n.title = title_foo n.title_de = title2_de n.title_en = title2_en n.save() 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) def test_rule4_url_field(self): self._test_field(field_name='url', value1_de='http://www.google.de', value1_en='http://www.google.com', value2_de='http://www.google.at', value2_en='http://www.google.co.uk', value3='http://www.google.net') def test_rule4_email_field(self): self._test_field(field_name='email', value1_de='django-modeltranslation@googlecode.de', value1_en='django-modeltranslation@googlecode.com', value2_de='django-modeltranslation@googlecode.at', value2_en='django-modeltranslation@googlecode.co.uk', value3='django-modeltranslation@googlecode.net') class ModelValidationTest(ModeltranslationTestBase): """ Tests if a translation model field validates correctly. """ def _test_model_validation(self, field_name, invalid_value, valid_value, invalid_value_de): """ Generic model field validation test. """ field_name_de = '%s_de' % field_name params = {'title_de': 'title de', 'title_en': 'title en', field_name: invalid_value} has_error_key = False # Create an object with an invalid url #n = TestModel.objects.create(title='Title', url='foo') n = TestModel.objects.create(**params) # First check the original field # Expect that the validation object contains an error for url try: n.full_clean() except ValidationError, e: if field_name in e.message_dict: has_error_key = True self.assertTrue(has_error_key) # Check the translation field # Language is set to 'de' at this point self.failUnlessEqual(get_language(), 'de') # Set translation field to a valid url #n.url_de = 'http://code.google.com/p/django-modeltranslation/' setattr(n, field_name_de, valid_value) has_error_key = False # Expect that the validation object contains no error for url try: n.full_clean() except ValidationError, e: if field_name_de in e.message_dict: has_error_key = True self.assertFalse(has_error_key) # Set translation field to an invalid url #n.url_de = 'foo' setattr(n, field_name_de, invalid_value) has_error_key = False # Expect that the validation object contains an error for url_de try: n.full_clean() except ValidationError, e: #if 'url_de' in e.message_dict: if field_name_de in e.message_dict: has_error_key = True self.assertTrue(has_error_key) def test_model_validation(self): """ General test for CharField and TextField. """ has_error_key = False # Create an object without title (which is required) n = TestModel.objects.create(text='Testtext') # First check the original field # Expect that the validation object contains an error for title try: n.full_clean() except ValidationError, e: if 'title' in e.message_dict: has_error_key = True self.assertTrue(has_error_key) n.save() # Check the translation field # Language is set to 'de' at this point self.failUnlessEqual(get_language(), 'de') # Set translation field to a valid title n.title_de = 'Title' has_error_key = False # Expect that the validation object contains no error for title try: n.full_clean() except ValidationError, e: if 'title_de' in e.message_dict: has_error_key = True self.assertFalse(has_error_key) # Set translation field to an empty title n.title_de = None has_error_key = False # Even though the original field isn't optional, translation fields are # per definition always optional. So we expect that the validation # object contains no error for title_de. try: n.full_clean() except ValidationError, e: if 'title_de' in e.message_dict: has_error_key = True self.assertFalse(has_error_key) def test_model_validation_url_field(self): #has_error_key = False ## Create an object with an invalid url #n = TestModel.objects.create(title='Title', url='foo') ## First check the original field ## Expect that the validation object contains an error for url #try: #n.full_clean() #except ValidationError, e: #if 'url' in e.message_dict: #has_error_key = True #self.assertTrue(has_error_key) ## Check the translation field ## Language is set to 'de' at this point #self.failUnlessEqual(get_language(), 'de') ## Set translation field to a valid url #n.url_de = 'http://code.google.com/p/django-modeltranslation/' #has_error_key = False ## Expect that the validation object contains no error for url #try: #n.full_clean() #except ValidationError, e: #if 'url_de' in e.message_dict: #has_error_key = True #self.assertFalse(has_error_key) ## Set translation field to an invalid url #n.url_de = 'foo' #has_error_key = False ## Expect that the validation object contains an error for url_de #try: #n.full_clean() #except ValidationError, e: #if 'url_de' in e.message_dict: #has_error_key = True #self.assertTrue(has_error_key) self._test_model_validation( field_name='url', invalid_value='foo en', valid_value='http://code.google.com/p/django-modeltranslation/', invalid_value_de='foo de') def test_model_validation_email_field(self): self._test_model_validation( field_name='email', invalid_value='foo en', valid_value='django-modeltranslation@googlecode.com', invalid_value_de='foo de') class ModelInheritanceTest(ModeltranslationTestBase): """Tests for inheritance support in modeltranslation.""" def test_abstract_inheritance(self): field_names_b = AbstractModelB._meta.get_all_field_names() self.failIf('titled' in field_names_b) self.failIf('titled_de' in field_names_b) self.failIf('titled_en' in field_names_b) def test_multitable_inheritance(self): field_names_a = MultitableModelA._meta.get_all_field_names() self.failUnless('titlea' in field_names_a) self.failUnless('titlea_de' in field_names_a) self.failUnless('titlea_en' in field_names_a) field_names_b = MultitableBModelA._meta.get_all_field_names() self.failUnless('titlea' in field_names_b) self.failUnless('titlea_de' in field_names_b) self.failUnless('titlea_en' in field_names_b) self.failUnless('titleb' in field_names_b) self.failUnless('titleb_de' in field_names_b) self.failUnless('titleb_en' in field_names_b) field_names_c = MultitableModelC._meta.get_all_field_names() self.failUnless('titlea' in field_names_c) self.failUnless('titlea_de' in field_names_c) self.failUnless('titlea_en' in field_names_c) self.failUnless('titleb' in field_names_c) self.failUnless('titleb_de' in field_names_c) self.failUnless('titleb_en' in field_names_c) self.failUnless('titlec' in field_names_c) self.failUnless('titlec_de' in field_names_c) self.failUnless('titlec_en' in field_names_c) field_names_d = MultitableDTestModel._meta.get_all_field_names() self.failUnless('titlea' in field_names_d) self.failUnless('titlea_de' in field_names_d) self.failUnless('titlea_en' in field_names_d) self.failUnless('titleb' in field_names_d) self.failUnless('titleb_de' in field_names_d) self.failUnless('titleb_en' in field_names_d) self.failUnless('titled' in field_names_d) class TranslationAdminTest(ModeltranslationTestBase): def setUp(self): trans_real.activate('de') self.test_obj = TestModel.objects.create( title='Testtitle', text='Testtext') self.site = AdminSite() def tearDown(self): trans_real.deactivate() self.test_obj.delete() def test_default_fields(self): class TestModelAdmin(TranslationAdmin): pass ma = TestModelAdmin(TestModel, self.site) self.assertEqual( ma.get_form(request).base_fields.keys(), ['title_de', 'title_en', 'text_de', 'text_en', 'url_de', 'url_en', 'email_de', 'email_en']) def test_default_fieldsets(self): class TestModelAdmin(TranslationAdmin): pass ma = TestModelAdmin(TestModel, self.site) # We expect that the original field is excluded and only the # translation fields are included in fields fields = ['title_de', 'title_en', 'text_de', 'text_en', 'url_de', 'url_en', 'email_de', 'email_en'] self.assertEqual( ma.get_fieldsets(request), [(None, {'fields': fields})]) self.assertEqual( ma.get_fieldsets(request, self.test_obj), [(None, {'fields': fields})]) def test_field_arguments(self): class TestModelAdmin(TranslationAdmin): fields = ['title'] ma = TestModelAdmin(TestModel, self.site) fields = ['title_de', 'title_en'] self.assertEqual(ma.get_form(request).base_fields.keys(), fields) self.assertEqual( ma.get_form(request, self.test_obj).base_fields.keys(), fields) def test_field_arguments_restricted_on_form(self): # Using `fields`. class TestModelAdmin(TranslationAdmin): fields = ['title'] ma = TestModelAdmin(TestModel, self.site) fields = ['title_de', 'title_en'] self.assertEqual(ma.get_form(request).base_fields.keys(), fields) self.assertEqual( ma.get_form(request, self.test_obj).base_fields.keys(), fields) # Using `fieldsets`. class TestModelAdmin(TranslationAdmin): fieldsets = [(None, {'fields': ['title']})] ma = TestModelAdmin(TestModel, self.site) self.assertEqual(ma.get_form(request).base_fields.keys(), fields) self.assertEqual( ma.get_form(request, self.test_obj).base_fields.keys(), fields) # Using `exclude`. class TestModelAdmin(TranslationAdmin): exclude = ['url', 'email'] ma = TestModelAdmin(TestModel, self.site) fields = ['title_de', 'title_en', 'text_de', 'text_en'] self.assertEqual( ma.get_form(request).base_fields.keys(), fields) # You can also pass a tuple to `exclude`. class TestModelAdmin(TranslationAdmin): exclude = ('url', 'email') ma = TestModelAdmin(TestModel, self.site) self.assertEqual( ma.get_form(request).base_fields.keys(), fields) self.assertEqual( ma.get_form(request, self.test_obj).base_fields.keys(), fields) # Using `fields` and `exclude`. class TestModelAdmin(TranslationAdmin): fields = ['title', 'url'] exclude = ['url'] ma = TestModelAdmin(TestModel, self.site) self.assertEqual( ma.get_form(request).base_fields.keys(), ['title_de', 'title_en']) def test_field_arguments_restricted_on_custom_form(self): # Using `fields`. class TestModelForm(forms.ModelForm): class Meta: model = TestModel fields = ['url', 'email'] class TestModelAdmin(TranslationAdmin): form = TestModelForm ma = TestModelAdmin(TestModel, self.site) fields = ['url_de', 'url_en', 'email_de', 'email_en'] self.assertEqual( ma.get_form(request).base_fields.keys(), fields) self.assertEqual( ma.get_form(request, self.test_obj).base_fields.keys(), fields) # Using `exclude`. class TestModelForm(forms.ModelForm): class Meta: model = TestModel exclude = ['url', 'email'] class TestModelAdmin(TranslationAdmin): form = TestModelForm ma = TestModelAdmin(TestModel, self.site) fields = ['title_de', 'title_en', 'text_de', 'text_en'] self.assertEqual( ma.get_form(request).base_fields.keys(), fields) self.assertEqual( ma.get_form(request, self.test_obj).base_fields.keys(), fields) # If both, the custom form an the ModelAdmin define an `exclude` # option, the ModelAdmin wins. This is Django behaviour. class TestModelAdmin(TranslationAdmin): form = TestModelForm exclude = ['url'] ma = TestModelAdmin(TestModel, self.site) fields = ['title_de', 'title_en', 'text_de', 'text_en', 'email_de', 'email_en'] self.assertEqual( ma.get_form(request).base_fields.keys(), fields) self.assertEqual( ma.get_form(request, self.test_obj).base_fields.keys(), fields) # Same for `fields`. class TestModelForm(forms.ModelForm): class Meta: model = TestModel fields = ['text', 'title'] class TestModelAdmin(TranslationAdmin): form = TestModelForm fields = ['email'] ma = TestModelAdmin(TestModel, self.site) fields = ['email_de', 'email_en'] self.assertEqual( ma.get_form(request).base_fields.keys(), fields) self.assertEqual( ma.get_form(request, self.test_obj).base_fields.keys(), fields) def test_inline_fieldsets(self): class DataInline(TranslationStackedInline): model = DataModel fieldsets = [ ('Test', {'fields': ('data',)}) ] class TestModelAdmin(TranslationAdmin): exclude = ('title', 'text',) inlines = [DataInline] class DataTranslationOptions(translator.TranslationOptions): fields = ('data',) translator.translator.register(DataModel, DataTranslationOptions) ma = TestModelAdmin(TestModel, self.site) fieldsets = [('Test', {'fields': ['data_de', 'data_en']})] try: ma_fieldsets = ma.get_inline_instances( request)[0].get_fieldsets(request) except AttributeError: # Django 1.3 fallback ma_fieldsets = ma.inlines[0]( TestModel, self.site).get_fieldsets(request) self.assertEqual(ma_fieldsets, fieldsets) try: ma_fieldsets = ma.get_inline_instances( request)[0].get_fieldsets(request, self.test_obj) except AttributeError: # Django 1.3 fallback 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) # Language suffixed version wins n = ManagerTestModel.objects.create(title='bar', 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)