# -*- coding: utf-8 -*-
import datetime
from decimal import Decimal
import imp
import os
import shutil

import django
from django import forms
from django.conf import settings as django_settings
from django.contrib.admin.sites import AdminSite
from django.core.exceptions import ValidationError, ImproperlyConfigured
from django.core.files.base import ContentFile
from django.core.files.storage import default_storage
from django.core.management import call_command
from django.db import IntegrityError
from django.db.models import Q, F, Count
from django.test import TestCase, TransactionTestCase
from django.test.utils import override_settings
from django.utils import six
from django.utils.translation import get_language, override, trans_real

try:
    from django.apps import apps as django_apps
    NEW_APP_CACHE = True
except ImportError:
    from django.db.models.loading import AppCache
    NEW_APP_CACHE = False

try:
    from unittest import skipUnless
except ImportError:
    # Dummy replacement for Python 2.6
    def skipUnless(condition, reason):
        if not condition:
            def decorator(test_item):
                return lambda s: 42
            return decorator
        return lambda x: x  # identity

from modeltranslation import admin, settings as mt_settings, translator
from modeltranslation.forms import TranslationModelForm
from modeltranslation.models import autodiscover
from modeltranslation.tests.test_settings import TEST_SETTINGS
from modeltranslation.utils import (build_css_class, build_localized_fieldname,
                                    auto_populate, fallbacks)

MIGRATE_CMD = django.VERSION >= (1, 8)
MIGRATIONS = MIGRATE_CMD and "django.contrib.auth" in TEST_SETTINGS['INSTALLED_APPS']
NEW_DEFERRED_API = django.VERSION >= (1, 10)

models = translation = None

# None of the following tests really depend on the content of the request,
# so we'll just pass in None.
request = None

# How many models are registered for tests.
TEST_MODELS = 31 + (1 if MIGRATIONS else 0)


class reload_override_settings(override_settings):
    """Context manager that not only override settings, but also reload modeltranslation conf."""
    def __enter__(self):
        super(reload_override_settings, self).__enter__()
        imp.reload(mt_settings)

    def __exit__(self, exc_type, exc_value, traceback):
        super(reload_override_settings, self).__exit__(exc_type, exc_value, traceback)
        imp.reload(mt_settings)


# In this test suite fallback language is turned off. This context manager temporarily turns it on.
def default_fallback():
    return reload_override_settings(
        MODELTRANSLATION_FALLBACK_LANGUAGES=(mt_settings.DEFAULT_LANGUAGE,))


class dummy_context_mgr():
    def __enter__(self):
        return None

    def __exit__(self, _type, value, traceback):
        return False


def get_field_names(model):
    if django.VERSION < (1, 9):
        return model._meta.get_all_field_names()
    names = set()
    fields = model._meta.get_fields()
    for field in fields:
        if field.is_relation and field.many_to_one and field.related_model is None:
            continue
        if field.model != model and field.model._meta.concrete_model == model._meta.concrete_model:
            continue

        names.add(field.name)
        if hasattr(field, 'attname'):
            names.add(field.attname)
    return names


@override_settings(**TEST_SETTINGS)
class ModeltranslationTransactionTestBase(TransactionTestCase):
    cache = django_apps if NEW_APP_CACHE else 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(ModeltranslationTransactionTestBase, cls).setUpClass()
        if not ModeltranslationTransactionTestBase.synced:
            # In order to perform only one syncdb
            ModeltranslationTransactionTestBase.synced = True
            mgr = (override_settings(**TEST_SETTINGS) if django.VERSION < (1, 8)
                   else dummy_context_mgr())
            with mgr:
                # 0. Render initial migration of auth
                from django.db import connections, DEFAULT_DB_ALIAS
                if MIGRATIONS:
                    call_command('makemigrations', 'auth', verbosity=2, interactive=False,
                                 database=connections[DEFAULT_DB_ALIAS].alias)

                # 1. Reload translation in case USE_I18N was False
                from django.utils import translation as dj_trans
                imp.reload(dj_trans)

                # 2. Reload MT because LANGUAGES likely changed.
                imp.reload(mt_settings)
                imp.reload(translator)
                imp.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)
                if not NEW_APP_CACHE:
                    cls.cache.load_app('modeltranslation.tests')
                else:
                    del cls.cache.all_models['tests']
                    if MIGRATIONS:
                        del cls.cache.all_models['auth']
                    import sys
                    sys.modules.pop('modeltranslation.tests.models', None)
                    sys.modules.pop('modeltranslation.tests.translation', None)
                    if MIGRATIONS:
                        sys.modules.pop('django.contrib.auth.models', None)
                    tests_args = []
                    if django.VERSION < (1, 11):
                        tests_args = [cls.cache.all_models['tests']]
                    cls.cache.get_app_config('tests').import_models(*tests_args)
                    if MIGRATIONS:
                        auth_args = []
                        if django.VERSION < (1, 11):
                            auth_args = [cls.cache.all_models['auth']]
                        cls.cache.get_app_config('auth').import_models(*auth_args)

                # 4. Autodiscover
                from modeltranslation.models import handle_translation_registrations
                handle_translation_registrations()

                # 5. makemigrations (``migrate=False`` in case of south)
                if MIGRATIONS:
                    call_command('makemigrations', 'auth', verbosity=2, interactive=False,
                                 database=connections[DEFAULT_DB_ALIAS].alias)

                # 6. Syncdb (``migrate=False`` in case of south)
                cmd = 'migrate' if MIGRATE_CMD else 'syncdb'
                call_command(cmd, verbosity=0, migrate=False, interactive=False, run_syncdb=True,
                             database=connections[DEFAULT_DB_ALIAS].alias, load_initial_data=False)

                # 7. clean migrations
                if MIGRATIONS:
                    import glob
                    dir = os.path.join(os.path.dirname(os.path.abspath(__file__)),
                                       "auth_migrations")
                    for f in glob.glob(dir + "/000?_*.py*"):
                        os.unlink(f)

                # A rather dirty trick to import models into module namespace, but not before
                # tests app has been added into INSTALLED_APPS and loaded
                # (that's why this is not imported in normal import section)
                global models, translation
                from modeltranslation.tests import models, translation  # NOQA

    def setUp(self):
        self._old_language = get_language()
        trans_real.activate('de')

    def tearDown(self):
        trans_real.activate(self._old_language)


class ModeltranslationTestBase(TestCase, ModeltranslationTransactionTestBase):
    pass


class TestAutodiscover(ModeltranslationTestBase):
    # The way the ``override_settings`` works on ``TestCase`` is wicked;
    # it patches ``_pre_setup`` and ``_post_teardown`` methods.
    # Because of this, if class B extends class A and both are ``override_settings``'ed,
    # class B settings would be overwritten by class A settings (if some keys clash).
    # To solve this, override some settings after parents ``_pre_setup`` is called.
    def _pre_setup(self):
        super(TestAutodiscover, self)._pre_setup()
        # Add test_app to INSTALLED_APPS
        new_installed_apps = django_settings.INSTALLED_APPS + ('modeltranslation.tests.test_app',)
        self.__override = override_settings(INSTALLED_APPS=new_installed_apps)
        self.__override.enable()

    def _post_teardown(self):
        self.__override.disable()
        imp.reload(mt_settings)  # restore mt_settings.FALLBACK_LANGUAGES
        super(TestAutodiscover, self)._post_teardown()

    @classmethod
    def setUpClass(cls):
        """Save registry (and restore it after tests)."""
        super(TestAutodiscover, cls).setUpClass()
        from copy import copy
        from modeltranslation.translator import translator
        cls.registry_cpy = copy(translator._registry)

    @classmethod
    def tearDownClass(cls):
        from modeltranslation.translator import translator
        translator._registry = cls.registry_cpy
        super(TestAutodiscover, cls).tearDownClass()

    def tearDown(self):
        import sys
        # Rollback model classes
        if NEW_APP_CACHE:
            del self.cache.all_models['test_app']
        else:
            del self.cache.app_models['test_app']
        from .test_app import models
        imp.reload(models)
        # Delete translation modules from import cache
        sys.modules.pop('modeltranslation.tests.test_app.translation', None)
        sys.modules.pop('modeltranslation.tests.project_translation', None)
        super(TestAutodiscover, self).tearDown()

    def check_news(self):
        from .test_app.models import News
        fields = dir(News())
        self.assertIn('title', fields)
        self.assertIn('title_en', fields)
        self.assertIn('title_de', fields)
        self.assertIn('visits', fields)
        self.assertNotIn('visits_en', fields)
        self.assertNotIn('visits_de', fields)

    def check_other(self, present=True):
        from .test_app.models import Other
        fields = dir(Other())
        self.assertIn('name', fields)
        if present:
            self.assertIn('name_en', fields)
            self.assertIn('name_de', fields)
        else:
            self.assertNotIn('name_en', fields)
            self.assertNotIn('name_de', fields)

    def test_simple(self):
        """Check if translation is imported for installed apps."""
        autodiscover()
        self.check_news()
        self.check_other(present=False)

    @reload_override_settings(
        MODELTRANSLATION_TRANSLATION_FILES=('modeltranslation.tests.project_translation',)
    )
    def test_global(self):
        """Check if translation is imported for global translation file."""
        autodiscover()
        self.check_news()
        self.check_other()

    @reload_override_settings(
        MODELTRANSLATION_TRANSLATION_FILES=('modeltranslation.tests.test_app.translation',)
    )
    def test_duplication(self):
        """Check if there is no problem with duplicated filenames."""
        autodiscover()
        self.check_news()


class ModeltranslationTest(ModeltranslationTestBase):
    """Basic tests for the modeltranslation application."""
    def test_registration(self):
        langs = tuple(l[0] for l in django_settings.LANGUAGES)
        self.assertEqual(langs, tuple(mt_settings.AVAILABLE_LANGUAGES))
        self.assertEqual(2, len(langs))
        self.assertTrue('de' in langs)
        self.assertTrue('en' in langs)
        self.assertTrue(translator.translator)

        # Check that all models are registered for translation
        self.assertEqual(len(translator.translator.get_registered_models()), TEST_MODELS)

        # Try to unregister a model that is not registered
        self.assertRaises(translator.NotRegistered,
                          translator.translator.unregister, models.BasePage)

        # Try to get options for a model that is not registered
        self.assertRaises(translator.NotRegistered,
                          translator.translator.get_options_for_model, models.ThirdPartyModel)

        # Ensure that a base can't be registered after a subclass.
        self.assertRaises(translator.DescendantRegistered,
                          translator.translator.register, models.BasePage)

        # Or unregistered before it.
        self.assertRaises(translator.DescendantRegistered,
                          translator.translator.unregister, models.Slugged)

    @skipUnless(NEW_DEFERRED_API, "Django 1.10 needed")
    def test_registration_field_conflicts(self):
        before = len(translator.translator.get_registered_models())

        # Exception should be raised when conflicting field name detected
        self.assertRaises(ValueError, translator.translator.register,
                          models.ConflictModel, fields=('title',))
        self.assertRaises(ValueError, translator.translator.register,
                          models.AbstractConflictModelB, fields=('title',))
        self.assertRaises(ValueError, translator.translator.register,
                          models.MultitableConflictModelB, fields=('title',))

        # Model should not be registered
        self.assertEqual(len(translator.translator.get_registered_models()), before)

    def test_fields(self):
        field_names = dir(models.TestModel())
        self.assertTrue('id' in field_names)
        self.assertTrue('title' in field_names)
        self.assertTrue('title_de' in field_names)
        self.assertTrue('title_en' in field_names)
        self.assertTrue('text' in field_names)
        self.assertTrue('text_de' in field_names)
        self.assertTrue('text_en' in field_names)
        self.assertTrue('url' in field_names)
        self.assertTrue('url_de' in field_names)
        self.assertTrue('url_en' in field_names)
        self.assertTrue('email' in field_names)
        self.assertTrue('email_de' in field_names)
        self.assertTrue('email_en' in field_names)

    def test_verbose_name(self):
        verbose_name = models.TestModel._meta.get_field('title_de').verbose_name
        self.assertEqual(six.text_type(verbose_name), 'title [de]')

    def test_descriptor_introspection(self):
        # See Django #8248
        try:
            models.TestModel.title
            models.TestModel.title.__doc__
            self.assertTrue(True)
        except:
            self.fail('Descriptor accessed on class should return itself.')

    def test_fields_hashes(self):
        opts = models.TestModel._meta
        orig = opts.get_field('title')
        en = opts.get_field('title_en')
        de = opts.get_field('title_de')
        # Translation field retain creation_counters
        self.assertEqual(orig.creation_counter, en.creation_counter)
        self.assertEqual(orig.creation_counter, de.creation_counter)
        # But they compare unequal
        self.assertNotEqual(orig, en)
        self.assertNotEqual(orig, de)
        self.assertNotEqual(en, de)
        # Their hashes too
        self.assertNotEqual(hash(orig), hash(en))
        self.assertNotEqual(hash(orig), hash(de))
        self.assertNotEqual(hash(en), hash(de))
        self.assertEqual(3, len(set([orig, en, de])))
        # TranslationFields can compare equal if they have the same language
        de.language = 'en'
        self.assertNotEqual(orig, de)
        self.assertEqual(en, de)
        self.assertEqual(hash(en), hash(de))
        self.assertEqual(2, len(set([orig, en, de])))
        de.language = 'de'

    def test_set_translation(self):
        """This test briefly shows main modeltranslation features."""
        self.assertEqual(get_language(), 'de')
        title_de = "title de"
        title_en = "title en"

        # The original field "title" passed in the constructor is
        # populated for the current language field: "title_de".
        inst2 = models.TestModel(title=title_de)
        self.assertEqual(inst2.title, title_de)
        self.assertEqual(inst2.title_en, None)
        self.assertEqual(inst2.title_de, title_de)

        # So creating object is language-aware
        with override('en'):
            inst2 = models.TestModel(title=title_en)
            self.assertEqual(inst2.title, title_en)
            self.assertEqual(inst2.title_en, title_en)
            self.assertEqual(inst2.title_de, None)

        # Value from original field is presented in current language:
        inst2 = models.TestModel(title_de=title_de, title_en=title_en)
        self.assertEqual(inst2.title, title_de)
        with override('en'):
            self.assertEqual(inst2.title, title_en)

        # Changes made via original field affect current language field:
        inst2.title = 'foo'
        self.assertEqual(inst2.title, 'foo')
        self.assertEqual(inst2.title_en, title_en)
        self.assertEqual(inst2.title_de, 'foo')
        with override('en'):
            inst2.title = 'bar'
            self.assertEqual(inst2.title, 'bar')
            self.assertEqual(inst2.title_en, 'bar')
            self.assertEqual(inst2.title_de, 'foo')
        self.assertEqual(inst2.title, 'foo')

        # When conflict, language field wins with original field
        inst2 = models.TestModel(title='foo', title_de=title_de, title_en=title_en)
        self.assertEqual(inst2.title, title_de)
        self.assertEqual(inst2.title_en, title_en)
        self.assertEqual(inst2.title_de, title_de)

        # Creating model and assigning only one language
        inst1 = models.TestModel(title_en=title_en)
        # Please note: '' and not None, because descriptor falls back to field default value
        self.assertEqual(inst1.title, '')
        self.assertEqual(inst1.title_en, title_en)
        self.assertEqual(inst1.title_de, None)
        # Assign current language value - de
        inst1.title = title_de
        self.assertEqual(inst1.title, title_de)
        self.assertEqual(inst1.title_en, title_en)
        self.assertEqual(inst1.title_de, title_de)
        inst1.save()

        # Check that the translation fields are correctly saved and provide the
        # correct value when retrieving them again.
        n = models.TestModel.objects.get(title=title_de)
        self.assertEqual(n.title, title_de)
        self.assertEqual(n.title_en, title_en)
        self.assertEqual(n.title_de, title_de)

        # Queries are also language-aware:
        self.assertEqual(1, models.TestModel.objects.filter(title=title_de).count())
        with override('en'):
            self.assertEqual(0, models.TestModel.objects.filter(title=title_de).count())

    def test_fallback_language(self):
        # Present what happens if current language field is empty
        self.assertEqual(get_language(), 'de')
        title_de = "title de"

        # Create model with value in de only...
        inst2 = models.TestModel(title=title_de)
        self.assertEqual(inst2.title, title_de)
        self.assertEqual(inst2.title_en, None)
        self.assertEqual(inst2.title_de, title_de)

        # In this test environment, fallback language is not set. So return value for en
        # will be field's default: ''
        with override('en'):
            self.assertEqual(inst2.title, '')
            self.assertEqual(inst2.title_en, None)  # Language field access returns real value

        # However, by default FALLBACK_LANGUAGES is set to DEFAULT_LANGUAGE
        with default_fallback():

            # No change here...
            self.assertEqual(inst2.title, title_de)

            # ... but for empty en fall back to de
            with override('en'):
                self.assertEqual(inst2.title, title_de)
                self.assertEqual(inst2.title_en, None)  # Still real value

    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 = models.FallbackModel(title=title1_de)
        n.save()
        n = models.FallbackModel.objects.get(title=title1_de)
        self.assertEqual(n.title, title1_de)
        trans_real.activate("en")
        self.assertEqual(n.title, "fallback")

    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 = models.FallbackModel2(title=title1_de, text=text1_de)
        n.save()
        n = models.FallbackModel2.objects.get(title=title1_de)
        trans_real.activate("en")
        self.assertEqual(n.title, '')  # Falling back to default field value
        self.assertEqual(
            n.text,
            translation.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 = models.TestModel(**keywords)
        m = models.TestModel.objects.create(**keywords)
        opts = translator.translator.get_options_for_model(models.TestModel)
        for base_field, trans_fields in opts.fields.items():
            self._compare_instances(n, m, base_field)
            for lang_field in trans_fields:
                self._compare_instances(n, m, lang_field.name)

    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 ModeltranslationTransactionTest(ModeltranslationTransactionTestBase):
    def test_unique_nullable_field(self):
        from django.db import transaction
        models.UniqueNullableModel.objects.create()
        models.UniqueNullableModel.objects.create()
        models.UniqueNullableModel.objects.create(title=None)
        models.UniqueNullableModel.objects.create(title=None)

        models.UniqueNullableModel.objects.create(title='')
        self.assertRaises(IntegrityError, models.UniqueNullableModel.objects.create, title='')
        transaction.rollback()  # Postgres
        models.UniqueNullableModel.objects.create(title='foo')
        self.assertRaises(IntegrityError, models.UniqueNullableModel.objects.create, title='foo')
        transaction.rollback()  # Postgres


class FallbackTests(ModeltranslationTestBase):
    test_fallback = {
        'default': ('de',),
        'de': ('en',)
    }

    def test_settings(self):
        # Initial
        self.assertEqual(mt_settings.FALLBACK_LANGUAGES, {'default': ()})
        # Tuple/list
        with reload_override_settings(MODELTRANSLATION_FALLBACK_LANGUAGES=('de',)):
            self.assertEqual(mt_settings.FALLBACK_LANGUAGES, {'default': ('de',)})
        # Whole dict
        with reload_override_settings(MODELTRANSLATION_FALLBACK_LANGUAGES=self.test_fallback):
            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: imp.reload(mt_settings))
        imp.reload(mt_settings)

    def test_resolution_order(self):
        from modeltranslation.utils import resolution_order
        with reload_override_settings(MODELTRANSLATION_FALLBACK_LANGUAGES=self.test_fallback):
            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 reload_override_settings(MODELTRANSLATION_FALLBACK_LANGUAGES=self.test_fallback):
            title_de = 'title de'
            title_en = 'title en'
            n = models.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 = models.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()

    def test_fallbacks_toggle(self):
        with reload_override_settings(MODELTRANSLATION_FALLBACK_LANGUAGES=self.test_fallback):
            m = models.TestModel(title='foo')
            with fallbacks(True):
                self.assertEqual(m.title_de, 'foo')
                self.assertEqual(m.title_en, None)
                self.assertEqual(m.title, 'foo')
                with override('en'):
                    self.assertEqual(m.title, 'foo')
            with fallbacks(False):
                self.assertEqual(m.title_de, 'foo')
                self.assertEqual(m.title_en, None)
                self.assertEqual(m.title, 'foo')
                with override('en'):
                    self.assertEqual(m.title, '')  # '' is the default

    def test_fallback_undefined(self):
        """
        Checks if a sensible value is considered undefined and triggers
        fallbacks. Tests if the value can be overridden as documented.
        """
        with reload_override_settings(MODELTRANSLATION_FALLBACK_LANGUAGES=self.test_fallback):
            # Non-nullable CharField falls back on empty strings.
            m = models.FallbackModel(title_en='value', title_de='')
            with override('en'):
                self.assertEqual(m.title, 'value')
            with override('de'):
                self.assertEqual(m.title, 'value')

            # Nullable CharField does not fall back on empty strings.
            m = models.FallbackModel(description_en='value', description_de='')
            with override('en'):
                self.assertEqual(m.description, 'value')
            with override('de'):
                self.assertEqual(m.description, '')

            # Nullable CharField does fall back on None.
            m = models.FallbackModel(description_en='value', description_de=None)
            with override('en'):
                self.assertEqual(m.description, 'value')
            with override('de'):
                self.assertEqual(m.description, 'value')

            # The undefined value may be overridden.
            m = models.FallbackModel2(title_en='value', title_de='')
            with override('en'):
                self.assertEqual(m.title, 'value')
            with override('de'):
                self.assertEqual(m.title, '')
            m = models.FallbackModel2(title_en='value', title_de='no title')
            with override('en'):
                self.assertEqual(m.title, 'value')
            with override('de'):
                self.assertEqual(m.title, 'value')


class FileFieldsTest(ModeltranslationTestBase):

    def tearDown(self):
        if default_storage.exists('modeltranslation_tests'):
            # With FileSystemStorage uploading files creates a new directory,
            # that's not automatically removed upon their deletion.
            tests_dir = default_storage.path('modeltranslation_tests')
            if os.path.isdir(tests_dir):
                shutil.rmtree(tests_dir)
        super(FileFieldsTest, self).tearDown()

    def test_translated_models(self):
        field_names = dir(models.FileFieldsModel())
        self.assertTrue('id' in field_names)
        self.assertTrue('title' in field_names)
        self.assertTrue('title_de' in field_names)
        self.assertTrue('title_en' in field_names)
        self.assertTrue('file' in field_names)
        self.assertTrue('file_de' in field_names)
        self.assertTrue('file_en' in field_names)
        self.assertTrue('image' in field_names)
        self.assertTrue('image_de' in field_names)
        self.assertTrue('image_en' in field_names)

    def _file_factory(self, name, content):
        try:
            return ContentFile(content, name=name)
        except TypeError:  # In Django 1.3 ContentFile had no name parameter
            file = ContentFile(content)
            file.name = name
            return file

    def test_translated_models_instance(self):
        inst = models.FileFieldsModel(title="Testtitle")

        trans_real.activate("en")
        inst.title = 'title_en'
        inst.file = 'a_en'
        inst.file.save('b_en', ContentFile('file in english'))
        inst.image = self._file_factory('i_en.jpg', 'image in english')  # Direct assign

        trans_real.activate("de")
        inst.title = 'title_de'
        inst.file = 'a_de'
        inst.file.save('b_de', ContentFile('file in german'))
        inst.image = self._file_factory('i_de.jpg', 'image in german')

        inst.save()

        trans_real.activate("en")
        self.assertEqual(inst.title, 'title_en')
        self.assertTrue(inst.file.name.count('b_en') > 0)
        self.assertEqual(inst.file.read(), b'file in english')
        self.assertTrue(inst.image.name.count('i_en') > 0)
        self.assertEqual(inst.image.read(), b'image in english')

        # Check if file was actually created in the global storage.
        self.assertTrue(default_storage.exists(inst.file))
        self.assertTrue(inst.file.size > 0)
        self.assertTrue(default_storage.exists(inst.image))
        self.assertTrue(inst.image.size > 0)

        trans_real.activate("de")
        self.assertEqual(inst.title, 'title_de')
        self.assertTrue(inst.file.name.count('b_de') > 0)
        self.assertEqual(inst.file.read(), b'file in german')
        self.assertTrue(inst.image.name.count('i_de') > 0)
        self.assertEqual(inst.image.read(), b'image in german')

        inst.file_en.delete()
        inst.image_en.delete()
        inst.file_de.delete()
        inst.image_de.delete()

    def test_empty_field(self):
        from django.db.models.fields.files import FieldFile
        inst = models.FileFieldsModel()
        self.assertIsInstance(inst.file, FieldFile)
        self.assertIsInstance(inst.file2, FieldFile)
        inst.save()
        inst = models.FileFieldsModel.objects.all()[0]
        self.assertIsInstance(inst.file, FieldFile)
        self.assertIsInstance(inst.file2, FieldFile)

    def test_fallback(self):
        from django.db.models.fields.files import FieldFile
        with reload_override_settings(MODELTRANSLATION_FALLBACK_LANGUAGES=('en',)):
            self.assertEqual(get_language(), 'de')
            inst = models.FileFieldsModel()
            inst.file_de = ''
            inst.file_en = 'foo'
            inst.file2_de = ''
            inst.file2_en = 'bar'
            self.assertIsInstance(inst.file, FieldFile)
            self.assertIsInstance(inst.file2, FieldFile)
            self.assertEqual(inst.file.name, 'foo')
            self.assertEqual(inst.file2.name, 'bar')
            inst.save()
            inst = models.FileFieldsModel.objects.all()[0]
            self.assertIsInstance(inst.file, FieldFile)
            self.assertIsInstance(inst.file2, FieldFile)
            self.assertEqual(inst.file.name, 'foo')
            self.assertEqual(inst.file2.name, 'bar')


class ForeignKeyFieldsTest(ModeltranslationTestBase):
    @classmethod
    def setUpClass(cls):
        # 'model' attribute cannot be assigned to class in its definition,
        # because ``models`` module will be reloaded and hence class would use old model classes.
        super(ForeignKeyFieldsTest, cls).setUpClass()
        cls.model = models.ForeignKeyModel

    def test_translated_models(self):
        field_names = dir(self.model())
        self.assertTrue('id' in field_names)
        for f in ('test', 'test_de', 'test_en', 'optional', 'optional_en', 'optional_de'):
            self.assertTrue(f in field_names)
            self.assertTrue('%s_id' % f in field_names)

    def test_db_column_names(self):
        meta = self.model._meta

        # Make sure the correct database columns always get used:
        attname, col = meta.get_field('test').get_attname_column()
        self.assertEqual(attname, 'test_id')
        self.assertEqual(attname, col)

        attname, col = meta.get_field('test_en').get_attname_column()
        self.assertEqual(attname, 'test_en_id')
        self.assertEqual(attname, col)

        attname, col = meta.get_field('test_de').get_attname_column()
        self.assertEqual(attname, 'test_de_id')
        self.assertEqual(attname, col)

    def test_translated_models_instance(self):
        test_inst1 = models.TestModel(title_en='title1_en', title_de='title1_de')
        test_inst1.save()
        test_inst2 = models.TestModel(title_en='title2_en', title_de='title2_de')
        test_inst2.save()
        inst = self.model()

        trans_real.activate("de")
        inst.test = test_inst1
        inst.optional = None

        trans_real.activate("en")
        # Test assigning relation by ID:
        inst.optional_id = test_inst2.pk
        inst.save()

        trans_real.activate("de")
        self.assertEqual(inst.test_id, test_inst1.pk)
        self.assertEqual(inst.test.title, 'title1_de')
        self.assertEqual(inst.test_de_id, test_inst1.pk)
        self.assertEqual(inst.test_de.title, 'title1_de')
        self.assertEqual(inst.optional, None)

        # Test fallbacks:
        trans_real.activate("en")
        with default_fallback():
            self.assertEqual(inst.test_id, test_inst1.pk)
            self.assertEqual(inst.test.pk, test_inst1.pk)
            self.assertEqual(inst.test.title, 'title1_en')

        # Test English:
        self.assertEqual(inst.optional_id, test_inst2.pk)
        self.assertEqual(inst.optional.title, 'title2_en')
        self.assertEqual(inst.optional_en_id, test_inst2.pk)
        self.assertEqual(inst.optional_en.title, 'title2_en')

        # Test caching
        inst.test_en = test_inst2
        inst.save()
        trans_real.activate("de")
        self.assertEqual(inst.test, test_inst1)
        trans_real.activate("en")
        self.assertEqual(inst.test, test_inst2)

        # Check filtering in direct way + lookup spanning
        manager = self.model.objects
        trans_real.activate("de")
        self.assertEqual(manager.filter(test=test_inst1).count(), 1)
        self.assertEqual(manager.filter(test_en=test_inst1).count(), 0)
        self.assertEqual(manager.filter(test_de=test_inst1).count(), 1)
        self.assertEqual(manager.filter(test=test_inst2).count(), 0)
        self.assertEqual(manager.filter(test_en=test_inst2).count(), 1)
        self.assertEqual(manager.filter(test_de=test_inst2).count(), 0)
        self.assertEqual(manager.filter(test__title='title1_de').count(), 1)
        self.assertEqual(manager.filter(test__title='title1_en').count(), 0)
        self.assertEqual(manager.filter(test__title_en='title1_en').count(), 1)
        trans_real.activate("en")
        self.assertEqual(manager.filter(test=test_inst1).count(), 0)
        self.assertEqual(manager.filter(test_en=test_inst1).count(), 0)
        self.assertEqual(manager.filter(test_de=test_inst1).count(), 1)
        self.assertEqual(manager.filter(test=test_inst2).count(), 1)
        self.assertEqual(manager.filter(test_en=test_inst2).count(), 1)
        self.assertEqual(manager.filter(test_de=test_inst2).count(), 0)
        self.assertEqual(manager.filter(test__title='title2_en').count(), 1)
        self.assertEqual(manager.filter(test__title='title2_de').count(), 0)
        self.assertEqual(manager.filter(test__title_de='title2_de').count(), 1)

    def test_reverse_relations(self):
        test_inst = models.TestModel(title_en='title_en', title_de='title_de')
        test_inst.save()

        # Instantiate many 'ForeignKeyModel' instances:
        fk_inst_both = self.model(title_en='f_title_en', title_de='f_title_de',
                                  test_de=test_inst, test_en=test_inst)
        fk_inst_both.save()
        fk_inst_de = self.model(title_en='f_title_en', title_de='f_title_de',
                                test_de_id=test_inst.pk)
        fk_inst_de.save()
        fk_inst_en = self.model(title_en='f_title_en', title_de='f_title_de',
                                test_en=test_inst)
        fk_inst_en.save()

        fk_option_de = self.model.objects.create(optional_de=test_inst)
        fk_option_en = self.model.objects.create(optional_en=test_inst)

        # Check that the reverse accessors are created on the model:
        # Explicit related_name
        testmodel_fields = get_field_names(models.TestModel)
        testmodel_methods = dir(models.TestModel)
        self.assertIn('test_fks',    testmodel_fields)
        self.assertIn('test_fks_de', testmodel_fields)
        self.assertIn('test_fks_en', testmodel_fields)
        self.assertIn('test_fks',    testmodel_methods)
        self.assertIn('test_fks_de', testmodel_methods)
        self.assertIn('test_fks_en', testmodel_methods)
        # Implicit related_name: manager descriptor name != query field name
        self.assertIn('foreignkeymodel',    testmodel_fields)
        self.assertIn('foreignkeymodel_de', testmodel_fields)
        self.assertIn('foreignkeymodel_en', testmodel_fields)
        self.assertIn('foreignkeymodel_set',    testmodel_methods)
        self.assertIn('foreignkeymodel_set_de', testmodel_methods)
        self.assertIn('foreignkeymodel_set_en', testmodel_methods)

        # Check the German reverse accessor:
        self.assertIn(fk_inst_both, test_inst.test_fks_de.all())
        self.assertIn(fk_inst_de, test_inst.test_fks_de.all())
        self.assertNotIn(fk_inst_en, test_inst.test_fks_de.all())

        # Check the English reverse accessor:
        self.assertIn(fk_inst_both, test_inst.test_fks_en.all())
        self.assertIn(fk_inst_en, test_inst.test_fks_en.all())
        self.assertNotIn(fk_inst_de, test_inst.test_fks_en.all())

        # Check the default reverse accessor:
        trans_real.activate("de")
        self.assertIn(fk_inst_de,    test_inst.test_fks.all())
        self.assertNotIn(fk_inst_en, test_inst.test_fks.all())
        trans_real.activate("en")
        self.assertIn(fk_inst_en,    test_inst.test_fks.all())
        self.assertNotIn(fk_inst_de, test_inst.test_fks.all())

        # Check implicit related_name reverse accessor:
        self.assertIn(fk_option_en, test_inst.foreignkeymodel_set.all())

        # Check filtering in reverse way + lookup spanning:
        manager = models.TestModel.objects
        trans_real.activate("de")
        self.assertEqual(manager.filter(test_fks=fk_inst_both).count(), 1)
        self.assertEqual(manager.filter(test_fks=fk_inst_de).count(), 1)
        self.assertEqual(manager.filter(test_fks__id=fk_inst_de.pk).count(), 1)
        self.assertEqual(manager.filter(test_fks=fk_inst_en).count(), 0)
        self.assertEqual(manager.filter(test_fks_en=fk_inst_en).count(), 1)
        self.assertEqual(manager.filter(foreignkeymodel=fk_option_de).count(), 1)
        self.assertEqual(manager.filter(foreignkeymodel=fk_option_en).count(), 0)
        self.assertEqual(manager.filter(foreignkeymodel_en=fk_option_en).count(), 1)
        self.assertEqual(manager.filter(test_fks__title='f_title_de').distinct().count(), 1)
        self.assertEqual(manager.filter(test_fks__title='f_title_en').distinct().count(), 0)
        self.assertEqual(manager.filter(test_fks__title_en='f_title_en').distinct().count(), 1)
        trans_real.activate("en")
        self.assertEqual(manager.filter(test_fks=fk_inst_both).count(), 1)
        self.assertEqual(manager.filter(test_fks=fk_inst_en).count(), 1)
        self.assertEqual(manager.filter(test_fks__id=fk_inst_en.pk).count(), 1)
        self.assertEqual(manager.filter(test_fks=fk_inst_de).count(), 0)
        self.assertEqual(manager.filter(test_fks_de=fk_inst_de).count(), 1)
        self.assertEqual(manager.filter(foreignkeymodel=fk_option_en).count(), 1)
        self.assertEqual(manager.filter(foreignkeymodel=fk_option_de).count(), 0)
        self.assertEqual(manager.filter(foreignkeymodel_de=fk_option_de).count(), 1)
        self.assertEqual(manager.filter(test_fks__title='f_title_en').distinct().count(), 1)
        self.assertEqual(manager.filter(test_fks__title='f_title_de').distinct().count(), 0)
        self.assertEqual(manager.filter(test_fks__title_de='f_title_de').distinct().count(), 1)

        # Check assignment
        trans_real.activate("de")
        test_inst2 = models.TestModel(title_en='title_en', title_de='title_de')
        test_inst2.save()
        if django.VERSION >= (1, 9):
            test_inst2.test_fks.set((fk_inst_de, fk_inst_both))
            test_inst2.test_fks_en.set((fk_inst_en, fk_inst_both))
        else:
            test_inst2.test_fks = [fk_inst_de, fk_inst_both]
            test_inst2.test_fks_en = (fk_inst_en, fk_inst_both)
        self.assertEqual(fk_inst_both.test.pk, test_inst2.pk)
        self.assertEqual(fk_inst_both.test_id, test_inst2.pk)
        self.assertEqual(fk_inst_both.test_de, test_inst2)
        self.assertQuerysetsEqual(test_inst2.test_fks_de.all(), test_inst2.test_fks.all())
        self.assertIn(fk_inst_both, test_inst2.test_fks.all())
        self.assertIn(fk_inst_de, test_inst2.test_fks.all())
        self.assertNotIn(fk_inst_en, test_inst2.test_fks.all())
        trans_real.activate("en")
        self.assertQuerysetsEqual(test_inst2.test_fks_en.all(), test_inst2.test_fks.all())
        self.assertIn(fk_inst_both, test_inst2.test_fks.all())
        self.assertIn(fk_inst_en, test_inst2.test_fks.all())
        self.assertNotIn(fk_inst_de, test_inst2.test_fks.all())

    def test_non_translated_relation(self):
        non_de = models.NonTranslated.objects.create(title='title_de')
        non_en = models.NonTranslated.objects.create(title='title_en')

        fk_inst_both = self.model.objects.create(
            title_en='f_title_en', title_de='f_title_de', non_de=non_de, non_en=non_en)
        fk_inst_de = self.model.objects.create(non_de=non_de)
        fk_inst_en = self.model.objects.create(non_en=non_en)

        # Forward relation + spanning
        manager = self.model.objects
        trans_real.activate("de")
        self.assertEqual(manager.filter(non=non_de).count(), 2)
        self.assertEqual(manager.filter(non=non_en).count(), 0)
        self.assertEqual(manager.filter(non_en=non_en).count(), 2)
        self.assertEqual(manager.filter(non__title='title_de').count(), 2)
        self.assertEqual(manager.filter(non__title='title_en').count(), 0)
        self.assertEqual(manager.filter(non_en__title='title_en').count(), 2)
        trans_real.activate("en")
        self.assertEqual(manager.filter(non=non_en).count(), 2)
        self.assertEqual(manager.filter(non=non_de).count(), 0)
        self.assertEqual(manager.filter(non_de=non_de).count(), 2)
        self.assertEqual(manager.filter(non__title='title_en').count(), 2)
        self.assertEqual(manager.filter(non__title='title_de').count(), 0)
        self.assertEqual(manager.filter(non_de__title='title_de').count(), 2)

        # Reverse relation + spanning
        manager = models.NonTranslated.objects
        trans_real.activate("de")
        self.assertEqual(manager.filter(test_fks=fk_inst_both).count(), 1)
        self.assertEqual(manager.filter(test_fks=fk_inst_de).count(), 1)
        self.assertEqual(manager.filter(test_fks=fk_inst_en).count(), 0)
        self.assertEqual(manager.filter(test_fks_en=fk_inst_en).count(), 1)
        self.assertEqual(manager.filter(test_fks__title='f_title_de').count(), 1)
        self.assertEqual(manager.filter(test_fks__title='f_title_en').count(), 0)
        self.assertEqual(manager.filter(test_fks__title_en='f_title_en').count(), 1)
        trans_real.activate("en")
        self.assertEqual(manager.filter(test_fks=fk_inst_both).count(), 1)
        self.assertEqual(manager.filter(test_fks=fk_inst_en).count(), 1)
        self.assertEqual(manager.filter(test_fks=fk_inst_de).count(), 0)
        self.assertEqual(manager.filter(test_fks_de=fk_inst_de).count(), 1)
        self.assertEqual(manager.filter(test_fks__title='f_title_en').count(), 1)
        self.assertEqual(manager.filter(test_fks__title='f_title_de').count(), 0)
        self.assertEqual(manager.filter(test_fks__title_de='f_title_de').count(), 1)

    def test_indonesian(self):
        field = models.ForeignKeyModel._meta.get_field('test')
        self.assertNotEqual(field.attname, build_localized_fieldname(field.name, 'id'))

    def assertQuerysetsEqual(self, qs1, qs2):
        def pk(o):
            return o.pk

        return self.assertEqual(sorted(qs1, key=pk), sorted(qs2, key=pk))


class OneToOneFieldsTest(ForeignKeyFieldsTest):
    @classmethod
    def setUpClass(cls):
        # 'model' attribute cannot be assigned to class in its definition,
        # because ``models`` module will be reloaded and hence class would use old model classes.
        super(OneToOneFieldsTest, cls).setUpClass()
        cls.model = models.OneToOneFieldModel

    def test_uniqueness(self):
        test_inst1 = models.TestModel(title_en='title1_en', title_de='title1_de')
        test_inst1.save()
        inst = self.model()

        trans_real.activate("de")
        inst.test = test_inst1

        trans_real.activate("en")
        # That's ok, since test_en is different than test_de
        inst.test = test_inst1
        inst.save()

        # But this violates uniqueness constraint
        inst2 = self.model(test=test_inst1)
        self.assertRaises(IntegrityError, inst2.save)

    def test_reverse_relations(self):
        test_inst = models.TestModel(title_en='title_en', title_de='title_de')
        test_inst.save()

        # Instantiate many 'OneToOneFieldModel' instances:
        fk_inst_de = self.model(title_en='f_title_en', title_de='f_title_de',
                                test_de_id=test_inst.pk)
        fk_inst_de.save()
        fk_inst_en = self.model(title_en='f_title_en', title_de='f_title_de',
                                test_en=test_inst)
        fk_inst_en.save()

        fk_option_de = self.model.objects.create(optional_de=test_inst)
        fk_option_en = self.model.objects.create(optional_en=test_inst)

        # Check that the reverse accessors are created on the model:
        # Explicit related_name
        testmodel_fields = get_field_names(models.TestModel)
        testmodel_methods = dir(models.TestModel)
        self.assertIn('test_o2o',    testmodel_fields)
        self.assertIn('test_o2o_de', testmodel_fields)
        self.assertIn('test_o2o_en', testmodel_fields)
        self.assertIn('test_o2o',    testmodel_methods)
        self.assertIn('test_o2o_de', testmodel_methods)
        self.assertIn('test_o2o_en', testmodel_methods)
        # Implicit related_name
        self.assertIn('onetoonefieldmodel',    testmodel_fields)
        self.assertIn('onetoonefieldmodel_de', testmodel_fields)
        self.assertIn('onetoonefieldmodel_en', testmodel_fields)
        self.assertIn('onetoonefieldmodel',    testmodel_methods)
        self.assertIn('onetoonefieldmodel_de', testmodel_methods)
        self.assertIn('onetoonefieldmodel_en', testmodel_methods)

        # Check the German reverse accessor:
        self.assertEqual(fk_inst_de, test_inst.test_o2o_de)

        # Check the English reverse accessor:
        self.assertEqual(fk_inst_en, test_inst.test_o2o_en)

        # Check the default reverse accessor:
        trans_real.activate("de")
        self.assertEqual(fk_inst_de, test_inst.test_o2o)
        trans_real.activate("en")
        self.assertEqual(fk_inst_en, test_inst.test_o2o)

        # Check implicit related_name reverse accessor:
        self.assertEqual(fk_option_en, test_inst.onetoonefieldmodel)

        # Check filtering in reverse way + lookup spanning:
        manager = models.TestModel.objects
        trans_real.activate("de")
        self.assertEqual(manager.filter(test_o2o=fk_inst_de).count(), 1)
        self.assertEqual(manager.filter(test_o2o__id=fk_inst_de.pk).count(), 1)
        self.assertEqual(manager.filter(test_o2o=fk_inst_en).count(), 0)
        self.assertEqual(manager.filter(test_o2o_en=fk_inst_en).count(), 1)
        self.assertEqual(manager.filter(onetoonefieldmodel=fk_option_de).count(), 1)
        self.assertEqual(manager.filter(onetoonefieldmodel=fk_option_en).count(), 0)
        self.assertEqual(manager.filter(onetoonefieldmodel_en=fk_option_en).count(), 1)
        self.assertEqual(manager.filter(test_o2o__title='f_title_de').distinct().count(), 1)
        self.assertEqual(manager.filter(test_o2o__title='f_title_en').distinct().count(), 0)
        self.assertEqual(manager.filter(test_o2o__title_en='f_title_en').distinct().count(), 1)
        trans_real.activate("en")
        self.assertEqual(manager.filter(test_o2o=fk_inst_en).count(), 1)
        self.assertEqual(manager.filter(test_o2o__id=fk_inst_en.pk).count(), 1)
        self.assertEqual(manager.filter(test_o2o=fk_inst_de).count(), 0)
        self.assertEqual(manager.filter(test_o2o_de=fk_inst_de).count(), 1)
        self.assertEqual(manager.filter(onetoonefieldmodel=fk_option_en).count(), 1)
        self.assertEqual(manager.filter(onetoonefieldmodel=fk_option_de).count(), 0)
        self.assertEqual(manager.filter(onetoonefieldmodel_de=fk_option_de).count(), 1)
        self.assertEqual(manager.filter(test_o2o__title='f_title_en').distinct().count(), 1)
        self.assertEqual(manager.filter(test_o2o__title='f_title_de').distinct().count(), 0)
        self.assertEqual(manager.filter(test_o2o__title_de='f_title_de').distinct().count(), 1)

        # Check assignment
        trans_real.activate("de")
        test_inst2 = models.TestModel(title_en='title_en', title_de='title_de')
        test_inst2.save()
        test_inst2.test_o2o = fk_inst_de
        test_inst2.test_o2o_en = fk_inst_en

        self.assertEqual(fk_inst_de.test.pk, test_inst2.pk)
        self.assertEqual(fk_inst_de.test_id, test_inst2.pk)
        self.assertEqual(fk_inst_de.test_de, test_inst2)
        self.assertEqual(test_inst2.test_o2o_de, test_inst2.test_o2o)
        self.assertEqual(fk_inst_de, test_inst2.test_o2o)
        trans_real.activate("en")
        self.assertEqual(fk_inst_en.test.pk, test_inst2.pk)
        self.assertEqual(fk_inst_en.test_id, test_inst2.pk)
        self.assertEqual(fk_inst_en.test_en, test_inst2)
        self.assertEqual(test_inst2.test_o2o_en, test_inst2.test_o2o)
        self.assertEqual(fk_inst_en, test_inst2.test_o2o)

    def test_non_translated_relation(self):
        non_de = models.NonTranslated.objects.create(title='title_de')
        non_en = models.NonTranslated.objects.create(title='title_en')

        fk_inst_de = self.model.objects.create(
            title_en='f_title_en', title_de='f_title_de', non_de=non_de)
        fk_inst_en = self.model.objects.create(
            title_en='f_title_en2', title_de='f_title_de2', non_en=non_en)

        # Forward relation + spanning
        manager = self.model.objects
        trans_real.activate("de")
        self.assertEqual(manager.filter(non=non_de).count(), 1)
        self.assertEqual(manager.filter(non=non_en).count(), 0)
        self.assertEqual(manager.filter(non_en=non_en).count(), 1)
        self.assertEqual(manager.filter(non__title='title_de').count(), 1)
        self.assertEqual(manager.filter(non__title='title_en').count(), 0)
        self.assertEqual(manager.filter(non_en__title='title_en').count(), 1)
        trans_real.activate("en")
        self.assertEqual(manager.filter(non=non_en).count(), 1)
        self.assertEqual(manager.filter(non=non_de).count(), 0)
        self.assertEqual(manager.filter(non_de=non_de).count(), 1)
        self.assertEqual(manager.filter(non__title='title_en').count(), 1)
        self.assertEqual(manager.filter(non__title='title_de').count(), 0)
        self.assertEqual(manager.filter(non_de__title='title_de').count(), 1)

        # Reverse relation + spanning
        manager = models.NonTranslated.objects
        trans_real.activate("de")
        self.assertEqual(manager.filter(test_o2o=fk_inst_de).count(), 1)
        self.assertEqual(manager.filter(test_o2o=fk_inst_en).count(), 0)
        self.assertEqual(manager.filter(test_o2o_en=fk_inst_en).count(), 1)
        self.assertEqual(manager.filter(test_o2o__title='f_title_de').count(), 1)
        self.assertEqual(manager.filter(test_o2o__title='f_title_en').count(), 0)
        self.assertEqual(manager.filter(test_o2o__title_en='f_title_en').count(), 1)
        trans_real.activate("en")
        self.assertEqual(manager.filter(test_o2o=fk_inst_en).count(), 1)
        self.assertEqual(manager.filter(test_o2o=fk_inst_de).count(), 0)
        self.assertEqual(manager.filter(test_o2o_de=fk_inst_de).count(), 1)
        self.assertEqual(manager.filter(test_o2o__title='f_title_en2').count(), 1)
        self.assertEqual(manager.filter(test_o2o__title='f_title_de2').count(), 0)
        self.assertEqual(manager.filter(test_o2o__title_de='f_title_de2').count(), 1)


class OtherFieldsTest(ModeltranslationTestBase):
    def test_translated_models(self):
        inst = models.OtherFieldsModel.objects.create()
        field_names = dir(inst)
        self.assertTrue('id' in field_names)
        self.assertTrue('int' in field_names)
        self.assertTrue('int_de' in field_names)
        self.assertTrue('int_en' in field_names)
        self.assertTrue('boolean' in field_names)
        self.assertTrue('boolean_de' in field_names)
        self.assertTrue('boolean_en' in field_names)
        self.assertTrue('nullboolean' in field_names)
        self.assertTrue('nullboolean_de' in field_names)
        self.assertTrue('nullboolean_en' in field_names)
        self.assertTrue('csi' in field_names)
        self.assertTrue('csi_de' in field_names)
        self.assertTrue('csi_en' in field_names)
        self.assertTrue('ip' in field_names)
        self.assertTrue('ip_de' in field_names)
        self.assertTrue('ip_en' in field_names)
        self.assertTrue('genericip' in field_names)
        self.assertTrue('genericip_de' in field_names)
        self.assertTrue('genericip_en' in field_names)
        self.assertTrue('float' in field_names)
        self.assertTrue('float_de' in field_names)
        self.assertTrue('float_en' in field_names)
        self.assertTrue('decimal' in field_names)
        self.assertTrue('decimal_de' in field_names)
        self.assertTrue('decimal_en' in field_names)
        inst.delete()

    def test_translated_models_integer_instance(self):
        inst = models.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 = models.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 = models.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)

        inst.nullboolean = None
        self.assertEqual(None, inst.nullboolean)
        self.assertEqual(False, inst.nullboolean_de)
        self.assertEqual(None, inst.nullboolean_en)

    def test_translated_models_commaseparatedinteger_instance(self):
        inst = models.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)

    def test_translated_models_ipaddress_instance(self):
        inst = models.OtherFieldsModel()
        inst.ip = '192.0.1.42'
        self.assertEqual('de', get_language())
        self.assertEqual('192.0.1.42', inst.ip)
        self.assertEqual('192.0.1.42', inst.ip_de)
        self.assertEqual(None, inst.ip_en)

        inst.ip = '192.0.23.1'
        inst.save()
        self.assertEqual('192.0.23.1', inst.ip)
        self.assertEqual('192.0.23.1', inst.ip_de)
        self.assertEqual(None, inst.ip_en)

        trans_real.activate('en')
        inst.ip = '192.0.1.42'
        self.assertEqual('192.0.1.42', inst.ip)
        self.assertEqual('192.0.23.1', inst.ip_de)
        self.assertEqual('192.0.1.42', inst.ip_en)

        # Check if validation is preserved
        inst.ip = '1;2'
        self.assertRaises(ValidationError, inst.full_clean)

    def test_translated_models_genericipaddress_instance(self):
        inst = models.OtherFieldsModel()
        inst.genericip = '2a02:42fe::4'
        self.assertEqual('de', get_language())
        self.assertEqual('2a02:42fe::4', inst.genericip)
        self.assertEqual('2a02:42fe::4', inst.genericip_de)
        self.assertEqual(None, inst.genericip_en)

        inst.genericip = '2a02:23fe::4'
        inst.save()
        self.assertEqual('2a02:23fe::4', inst.genericip)
        self.assertEqual('2a02:23fe::4', inst.genericip_de)
        self.assertEqual(None, inst.genericip_en)

        trans_real.activate('en')
        inst.genericip = '2a02:42fe::4'
        self.assertEqual('2a02:42fe::4', inst.genericip)
        self.assertEqual('2a02:23fe::4', inst.genericip_de)
        self.assertEqual('2a02:42fe::4', inst.genericip_en)

        # Check if validation is preserved
        inst.genericip = '1;2'
        self.assertRaises(ValidationError, inst.full_clean)

    def test_translated_models_float_instance(self):
        inst = models.OtherFieldsModel()
        inst.float = 0.42
        self.assertEqual('de', get_language())
        self.assertEqual(0.42, inst.float)
        self.assertEqual(0.42, inst.float_de)
        self.assertEqual(None, inst.float_en)

        inst.float = 0.23
        inst.save()
        self.assertEqual(0.23, inst.float)
        self.assertEqual(0.23, inst.float_de)
        self.assertEqual(None, inst.float_en)

        inst.float += 0.08
        self.assertEqual(0.31, inst.float)
        self.assertEqual(0.31, inst.float_de)
        self.assertEqual(None, inst.float_en)

        trans_real.activate('en')
        inst.float = 0.42
        self.assertEqual(0.42, inst.float)
        self.assertEqual(0.31, inst.float_de)
        self.assertEqual(0.42, inst.float_en)

    def test_translated_models_decimal_instance(self):
        inst = models.OtherFieldsModel()
        inst.decimal = Decimal('0.42')
        self.assertEqual('de', get_language())
        self.assertEqual(Decimal('0.42'), inst.decimal)
        self.assertEqual(Decimal('0.42'), inst.decimal_de)
        self.assertEqual(None, inst.decimal_en)

        inst.decimal = inst.decimal - Decimal('0.19')
        inst.save()
        self.assertEqual(Decimal('0.23'), inst.decimal)
        self.assertEqual(Decimal('0.23'), inst.decimal_de)
        self.assertEqual(None, inst.decimal_en)

        trans_real.activate('en')
        self.assertRaises(TypeError, lambda x: inst.decimal + Decimal('0.19'))
        self.assertEqual(None, inst.decimal)
        self.assertEqual(Decimal('0.23'), inst.decimal_de)
        self.assertEqual(None, inst.decimal_en)

        inst.decimal = Decimal('0.42')
        self.assertEqual(Decimal('0.42'), inst.decimal)
        self.assertEqual(Decimal('0.23'), inst.decimal_de)
        self.assertEqual(Decimal('0.42'), inst.decimal_en)

    def test_translated_models_date_instance(self):
        inst = models.OtherFieldsModel()
        inst.date = datetime.date(2012, 12, 31)
        self.assertEqual('de', get_language())
        self.assertEqual(datetime.date(2012, 12, 31), inst.date)
        self.assertEqual(datetime.date(2012, 12, 31), inst.date_de)
        self.assertEqual(None, inst.date_en)

        inst.date = datetime.date(1999, 1, 1)
        inst.save()
        self.assertEqual(datetime.date(1999, 1, 1), inst.date)
        self.assertEqual(datetime.date(1999, 1, 1), inst.date_de)
        self.assertEqual(None, inst.date_en)

        qs = models.OtherFieldsModel.objects.filter(date='1999-1-1')
        self.assertEqual(len(qs), 1)
        self.assertEqual(qs[0].date, datetime.date(1999, 1, 1))

        trans_real.activate('en')
        inst.date = datetime.date(2012, 12, 31)
        self.assertEqual(datetime.date(2012, 12, 31), inst.date)
        self.assertEqual(datetime.date(1999, 1, 1), inst.date_de)
        self.assertEqual(datetime.date(2012, 12, 31), inst.date_en)

    def test_translated_models_datetime_instance(self):
        inst = models.OtherFieldsModel()
        inst.datetime = datetime.datetime(2012, 12, 31, 23, 42)
        self.assertEqual('de', get_language())
        self.assertEqual(datetime.datetime(2012, 12, 31, 23, 42), inst.datetime)
        self.assertEqual(datetime.datetime(2012, 12, 31, 23, 42), inst.datetime_de)
        self.assertEqual(None, inst.datetime_en)

        inst.datetime = datetime.datetime(1999, 1, 1, 23, 42)
        inst.save()
        self.assertEqual(datetime.datetime(1999, 1, 1, 23, 42), inst.datetime)
        self.assertEqual(datetime.datetime(1999, 1, 1, 23, 42), inst.datetime_de)
        self.assertEqual(None, inst.datetime_en)

        qs = models.OtherFieldsModel.objects.filter(datetime='1999-1-1 23:42')
        self.assertEqual(len(qs), 1)
        self.assertEqual(qs[0].datetime, datetime.datetime(1999, 1, 1, 23, 42))

        trans_real.activate('en')
        inst.datetime = datetime.datetime(2012, 12, 31, 23, 42)
        self.assertEqual(datetime.datetime(2012, 12, 31, 23, 42), inst.datetime)
        self.assertEqual(datetime.datetime(1999, 1, 1, 23, 42), inst.datetime_de)
        self.assertEqual(datetime.datetime(2012, 12, 31, 23, 42), inst.datetime_en)

    def test_translated_models_time_instance(self):
        inst = models.OtherFieldsModel()
        inst.time = datetime.time(23, 42, 0)
        self.assertEqual('de', get_language())
        self.assertEqual(datetime.time(23, 42, 0), inst.time)
        self.assertEqual(datetime.time(23, 42, 0), inst.time_de)
        self.assertEqual(None, inst.time_en)

        inst.time = datetime.time(1, 2, 3)
        inst.save()
        self.assertEqual(datetime.time(1, 2, 3), inst.time)
        self.assertEqual(datetime.time(1, 2, 3), inst.time_de)
        self.assertEqual(None, inst.time_en)

        qs = models.OtherFieldsModel.objects.filter(time='01:02:03')
        self.assertEqual(len(qs), 1)
        self.assertEqual(qs[0].time, datetime.time(1, 2, 3))

        trans_real.activate('en')
        inst.time = datetime.time(23, 42, 0)
        self.assertEqual(datetime.time(23, 42, 0), inst.time)
        self.assertEqual(datetime.time(1, 2, 3), inst.time_de)
        self.assertEqual(datetime.time(23, 42, 0), inst.time_en)

    def test_dates_queryset(self):
        Model = models.OtherFieldsModel

        Model.objects.create(datetime=datetime.datetime(2015, 9, 2, 0, 0))
        Model.objects.create(datetime=datetime.datetime(2014, 8, 3, 0, 0))
        Model.objects.create(datetime=datetime.datetime(2013, 7, 4, 0, 0))

        qs = Model.objects.dates('datetime', 'year', 'DESC')

        self.assertEqual(list(qs), [
            datetime.date(2015, 1, 1),
            datetime.date(2014, 1, 1),
            datetime.date(2013, 1, 1)
        ])

    def test_descriptors(self):
        # Descriptor store ints in database and returns string of 'a' of that length
        inst = models.DescriptorModel()
        # Demonstrate desired behaviour
        inst.normal = 2
        self.assertEqual('aa', inst.normal)
        inst.normal = 'abc'
        self.assertEqual('aaa', inst.normal)

        # Descriptor on translated field works too
        self.assertEqual('de', get_language())
        inst.trans = 5
        self.assertEqual('aaaaa', inst.trans)

        inst.save()
        db_values = models.DescriptorModel.objects.raw_values('normal', 'trans_en', 'trans_de')[0]
        self.assertEqual(3, db_values['normal'])
        self.assertEqual(5, db_values['trans_de'])
        self.assertEqual(0, db_values['trans_en'])

        # Retrieval from db
        inst = models.DescriptorModel.objects.all()[0]
        self.assertEqual('aaa', inst.normal)
        self.assertEqual('aaaaa', inst.trans)
        self.assertEqual('aaaaa', inst.trans_de)
        self.assertEqual('', inst.trans_en)

        # Other language
        trans_real.activate('en')
        self.assertEqual('', inst.trans)
        inst.trans = 'q'
        self.assertEqual('a', inst.trans)
        inst.trans_de = 4
        self.assertEqual('aaaa', inst.trans_de)
        inst.save()
        db_values = models.DescriptorModel.objects.raw_values('normal', 'trans_en', 'trans_de')[0]
        self.assertEqual(3, db_values['normal'])
        self.assertEqual(4, db_values['trans_de'])
        self.assertEqual(1, db_values['trans_en'])


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 = {field_name_de: value_de, field_name_en: value_en}

        n = models.TestModel.objects.create(**params)
        # Language is set to 'de' at this point
        self.assertEqual(get_language(), 'de')
        self.assertEqual(getattr(n, field_name), value_de)
        self.assertEqual(getattr(n, field_name_de), value_de)
        self.assertEqual(getattr(n, field_name_en), value_en)
        # Now switch to "en"
        trans_real.activate("en")
        self.assertEqual(get_language(), "en")
        # Should now be return the english one (just by switching the language)
        self.assertEqual(getattr(n, field_name), value_en)
        # But explicit language fields hold their values
        self.assertEqual(getattr(n, field_name_de), value_de)
        self.assertEqual(getattr(n, field_name_en), value_en)

        n = models.TestModel.objects.create(**params)
        n.save()
        # Language is set to "en" at this point
        self.assertEqual(get_language(), "en")
        self.assertEqual(getattr(n, field_name), value_en)
        self.assertEqual(getattr(n, field_name_de), value_de)
        self.assertEqual(getattr(n, field_name_en), value_en)
        trans_real.activate('de')
        self.assertEqual(get_language(), 'de')
        self.assertEqual(getattr(n, field_name), value_de)

        if deactivate:
            trans_real.deactivate()

    def test_rule1(self):
        """
        Basic CharField/TextField test.
        """
        title1_de = "title de"
        title1_en = "title en"
        text_de = "Dies ist ein deutscher Satz"
        text_en = "This is an english sentence"

        self._test_field(field_name='title', value_de=title1_de, value_en=title1_en)
        self._test_field(field_name='text', value_de=text_de, value_en=text_en)

    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 updates the value
    in the associated current language translation field.
    """
    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 = {field_name_de: value1_de, field_name_en: value1_en}

        self.assertEqual(get_language(), 'de')
        n = models.TestModel.objects.create(**params)
        self.assertEqual(getattr(n, field_name), value1_de)
        self.assertEqual(getattr(n, field_name_de), value1_de)
        self.assertEqual(getattr(n, field_name_en), value1_en)

        setattr(n, field_name, value2)
        n.save()
        self.assertEqual(getattr(n, field_name), value2)
        self.assertEqual(getattr(n, field_name_de), value2)
        self.assertEqual(getattr(n, field_name_en), value1_en)

        trans_real.activate("en")
        self.assertEqual(get_language(), "en")

        setattr(n, field_name, value3)
        setattr(n, field_name_de, value1_de)
        n.save()
        self.assertEqual(getattr(n, field_name), value3)
        self.assertEqual(getattr(n, field_name_en), value3)
        self.assertEqual(getattr(n, field_name_de), value1_de)

        if deactivate:
            trans_real.deactivate()

    def test_rule2(self):
        """
        Basic CharField/TextField test.
        """
        self._test_field(field_name='title',
                         value1_de='title de',
                         value1_en='title en',
                         value2='Neuer Titel',
                         value3='new title')

    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: If both fields - the original and the current language translation
    field - are updated at the same time, the current language translation
    field wins.
    """

    def test_rule3(self):
        self.assertEqual(get_language(), 'de')
        title = 'title de'

        # Normal behaviour
        n = models.TestModel(title='foo')
        self.assertEqual(n.title, 'foo')
        self.assertEqual(n.title_de, 'foo')
        self.assertEqual(n.title_en, None)

        # constructor
        n = models.TestModel(title_de=title, title='foo')
        self.assertEqual(n.title, title)
        self.assertEqual(n.title_de, title)
        self.assertEqual(n.title_en, None)

        # object.create
        n = models.TestModel.objects.create(title_de=title, title='foo')
        self.assertEqual(n.title, title)
        self.assertEqual(n.title_de, title)
        self.assertEqual(n.title_en, None)

        # Database save/load
        n = models.TestModel.objects.get(title_de=title)
        self.assertEqual(n.title, title)
        self.assertEqual(n.title_de, title)
        self.assertEqual(n.title_en, None)

        # This is not subject to Rule 3, because updates are not *at the ame time*
        n = models.TestModel()
        n.title_de = title
        n.title = 'foo'
        self.assertEqual(n.title, 'foo')
        self.assertEqual(n.title_de, 'foo')
        self.assertEqual(n.title_en, None)

    @staticmethod
    def _index(list, element):
        for i, el in enumerate(list):
            if el is element:
                return i
        raise ValueError

    def test_rule3_internals(self):
        # Rule 3 work because translation fields are added to model field list
        # later than original field.
        original = models.TestModel._meta.get_field('title')
        translated_de = models.TestModel._meta.get_field('title_de')
        translated_en = models.TestModel._meta.get_field('title_en')
        fields = models.TestModel._meta.fields
        # Here we cannot use simple list.index, because Field has overloaded __cmp__
        self.assertTrue(self._index(fields, original) < self._index(fields, translated_de))
        self.assertTrue(self._index(fields, original) < self._index(fields, translated_en))


class ModelValidationTest(ModeltranslationTestBase):
    """
    Tests if a translation model field validates correctly.
    """
    def assertRaisesValidation(self, func):
        try:
            func()
        except ValidationError as e:
            return e.message_dict
        self.fail('ValidationError not raised.')

    def _test_model_validation(self, field_name, invalid_value, valid_value):
        """
        Generic model field validation test.
        """
        field_name_de = '%s_de' % field_name
        field_name_en = '%s_en' % field_name
        # Title need to be passed here - otherwise it would not validate
        params = {'title_de': 'title de', 'title_en': 'title en', field_name: invalid_value}

        n = models.TestModel.objects.create(**params)

        # First check the original field
        # Expect that the validation object contains an error
        errors = self.assertRaisesValidation(n.full_clean)
        self.assertIn(field_name, errors)

        # Set translation field to a valid value
        # Language is set to 'de' at this point
        self.assertEqual(get_language(), 'de')
        setattr(n, field_name_de, valid_value)
        n.full_clean()

        # All language fields are validated even though original field validation raise no error
        setattr(n, field_name_en, invalid_value)
        errors = self.assertRaisesValidation(n.full_clean)
        self.assertNotIn(field_name, errors)
        self.assertIn(field_name_en, errors)

        # When language is changed to en, the original field also doesn't validate
        with override('en'):
            setattr(n, field_name_en, invalid_value)
            errors = self.assertRaisesValidation(n.full_clean)
            self.assertIn(field_name, errors)
            self.assertIn(field_name_en, errors)

        # Set translation field to an invalid value
        setattr(n, field_name_en, valid_value)
        setattr(n, field_name_de, invalid_value)
        # Expect that the validation object contains an error for url_de
        errors = self.assertRaisesValidation(n.full_clean)
        self.assertIn(field_name, errors)
        self.assertIn(field_name_de, errors)

    def test_model_validation_required(self):
        """
        General test for CharField: if required/blank is handled properly.
        """
        # Create an object without title (which is required)
        n = models.TestModel.objects.create(text='Testtext')

        # First check the original field
        # Expect that the validation object contains an error for title
        errors = self.assertRaisesValidation(n.full_clean)
        self.assertIn('title', errors)
        n.save()

        # Check the translation field
        # Language is set to 'de' at this point
        self.assertEqual(get_language(), 'de')
        # Set translation field to a valid title
        n.title_de = 'Title'
        n.full_clean()

        # Change language to en
        # Now validation fails, because current language (en) title is empty
        # So requirement validation depends on current language
        with override('en'):
            errors = self.assertRaisesValidation(n.full_clean)
            self.assertIn('title', errors)

            # However, with fallback language (most cases), it validates (because empty title
            # falls back to title_de):
            with default_fallback():
                n.full_clean()

        # Set translation field to an empty title
        n.title_de = None
        # 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.
        # However, title still raises error, since it points to empty title_de
        errors = self.assertRaisesValidation(n.full_clean)
        self.assertNotIn('title_de', errors)
        self.assertIn('title', errors)

    def test_model_validation_url_field(self):
        self._test_model_validation(
            field_name='url',
            invalid_value='foo en',
            valid_value='http://code.google.com/p/django-modeltranslation/')

    def test_model_validation_email_field(self):
        self._test_model_validation(
            field_name='email', invalid_value='foo en',
            valid_value='django-modeltranslation@googlecode.com')


class ModelInheritanceTest(ModeltranslationTestBase):
    """Tests for inheritance support in modeltranslation."""
    def test_abstract_inheritance(self):
        field_names_b = get_field_names(models.AbstractModelB)
        self.assertTrue('titlea' in field_names_b)
        self.assertTrue('titlea_de' in field_names_b)
        self.assertTrue('titlea_en' in field_names_b)
        self.assertTrue('titleb' in field_names_b)
        self.assertTrue('titleb_de' in field_names_b)
        self.assertTrue('titleb_en' in field_names_b)
        self.assertFalse('titled' in field_names_b)
        self.assertFalse('titled_de' in field_names_b)
        self.assertFalse('titled_en' in field_names_b)

    def test_multitable_inheritance(self):
        field_names_a = get_field_names(models.MultitableModelA)
        self.assertTrue('titlea' in field_names_a)
        self.assertTrue('titlea_de' in field_names_a)
        self.assertTrue('titlea_en' in field_names_a)

        field_names_b = get_field_names(models.MultitableModelB)
        self.assertTrue('titlea' in field_names_b)
        self.assertTrue('titlea_de' in field_names_b)
        self.assertTrue('titlea_en' in field_names_b)
        self.assertTrue('titleb' in field_names_b)
        self.assertTrue('titleb_de' in field_names_b)
        self.assertTrue('titleb_en' in field_names_b)

        field_names_c = get_field_names(models.MultitableModelC)
        self.assertTrue('titlea' in field_names_c)
        self.assertTrue('titlea_de' in field_names_c)
        self.assertTrue('titlea_en' in field_names_c)
        self.assertTrue('titleb' in field_names_c)
        self.assertTrue('titleb_de' in field_names_c)
        self.assertTrue('titleb_en' in field_names_c)
        self.assertTrue('titlec' in field_names_c)
        self.assertTrue('titlec_de' in field_names_c)
        self.assertTrue('titlec_en' in field_names_c)

        field_names_d = get_field_names(models.MultitableModelD)
        self.assertTrue('titlea' in field_names_d)
        self.assertTrue('titlea_de' in field_names_d)
        self.assertTrue('titlea_en' in field_names_d)
        self.assertTrue('titleb' in field_names_d)
        self.assertTrue('titleb_de' in field_names_d)
        self.assertTrue('titleb_en' in field_names_d)
        self.assertTrue('titled' in field_names_d)

    def test_inheritance(self):
        def assertLocalFields(model, local_fields):
            # Proper fields are inherited.
            opts = translator.translator.get_options_for_model(model)
            self.assertEqual(set(opts.local_fields.keys()), set(local_fields))
            # Local translation fields are created on the model.
            model_local_fields = [f.name for f in model._meta.local_fields]
            for field in local_fields:
                for lang in mt_settings.AVAILABLE_LANGUAGES:
                    translation_field = build_localized_fieldname(field, lang)
                    self.assertTrue(translation_field in model_local_fields)

        def assertFields(model, fields):
            # The given fields are inherited.
            opts = translator.translator.get_options_for_model(model)
            self.assertEqual(set(opts.fields.keys()), set(fields))
            # Inherited translation fields are available on the model.
            model_fields = get_field_names(model)
            for field in fields:
                for lang in mt_settings.AVAILABLE_LANGUAGES:
                    translation_field = build_localized_fieldname(field, lang)
                    self.assertTrue(translation_field in model_fields)

        # Translation fields can be declared on abstract classes.
        assertLocalFields(models.Slugged, ('slug',))
        assertLocalFields(models.MetaData, ('keywords',))
        assertLocalFields(models.RichText, ('content',))
        # Local fields are inherited from abstract superclasses.
        assertLocalFields(models.Displayable, ('slug', 'keywords',))
        assertLocalFields(models.Page, ('slug', 'keywords', 'title',))
        # But not from concrete superclasses.
        assertLocalFields(models.RichTextPage, ('content',))

        # Fields inherited from concrete models are also available.
        assertFields(models.Slugged, ('slug',))
        assertFields(models.Page, ('slug', 'keywords', 'title',))
        assertFields(models.RichTextPage, ('slug', 'keywords', 'title',
                                           'content',))


class ModelInheritanceFieldAggregationTest(ModeltranslationTestBase):
    """
    Tests for inheritance support with field aggregation
    in modeltranslation.
    """
    def test_field_aggregation(self):
        clsb = translation.FieldInheritanceCTranslationOptions
        self.assertTrue('titlea' in clsb.fields)
        self.assertTrue('titleb' in clsb.fields)
        self.assertTrue('titlec' in clsb.fields)
        self.assertEqual(3, len(clsb.fields))
        self.assertEqual(tuple, type(clsb.fields))

    def test_multi_inheritance(self):
        clsb = translation.FieldInheritanceETranslationOptions
        self.assertTrue('titlea' in clsb.fields)
        self.assertTrue('titleb' in clsb.fields)
        self.assertTrue('titlec' in clsb.fields)
        self.assertTrue('titled' in clsb.fields)
        self.assertTrue('titlee' in clsb.fields)
        self.assertEqual(5, len(clsb.fields))  # there are no repetitions


class UpdateCommandTest(ModeltranslationTestBase):
    def test_update_command(self):
        # Here it would be convenient to use fixtures - unfortunately,
        # fixtures loader doesn't use raw sql but rather creates objects,
        # so translation descriptor affects result and we cannot set the
        # 'original' field value.
        pk1 = models.TestModel.objects.create(title_de='').pk
        pk2 = models.TestModel.objects.create(title_de='already').pk
        # Due to ``rewrite(False)`` here, original field will be affected.
        models.TestModel.objects.all().rewrite(False).update(title='initial')

        # Check raw data using ``values``
        obj1 = models.TestModel.objects.filter(pk=pk1).raw_values()[0]
        obj2 = models.TestModel.objects.filter(pk=pk2).raw_values()[0]
        self.assertEqual('', obj1['title_de'])
        self.assertEqual('initial', obj1['title'])
        self.assertEqual('already', obj2['title_de'])
        self.assertEqual('initial', obj2['title'])

        call_command('update_translation_fields', verbosity=0)

        obj1 = models.TestModel.objects.get(pk=pk1)
        obj2 = models.TestModel.objects.get(pk=pk2)
        self.assertEqual('initial', obj1.title_de)
        self.assertEqual('already', obj2.title_de)


class TranslationAdminTest(ModeltranslationTestBase):
    def setUp(self):
        super(TranslationAdminTest, self).setUp()
        self.test_obj = models.TestModel.objects.create(
            title='Testtitle', text='Testtext')
        self.site = AdminSite()

    def tearDown(self):
        self.test_obj.delete()
        super(TranslationAdminTest, self).tearDown()

    def test_default_fields(self):
        class TestModelAdmin(admin.TranslationAdmin):
            pass

        ma = TestModelAdmin(models.TestModel, self.site)
        self.assertEqual(
            tuple(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(admin.TranslationAdmin):
            pass

        ma = TestModelAdmin(models.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(admin.TranslationAdmin):
            fields = ['title']

        ma = TestModelAdmin(models.TestModel, self.site)
        fields = ['title_de', 'title_en']
        self.assertEqual(tuple(ma.get_form(request).base_fields.keys()), tuple(fields))
        self.assertEqual(
            tuple(ma.get_form(request, self.test_obj).base_fields.keys()), tuple(fields))

    def test_field_arguments_restricted_on_form(self):
        # Using `fields`.
        class TestModelAdmin(admin.TranslationAdmin):
            fields = ['title']

        ma = TestModelAdmin(models.TestModel, self.site)
        fields = ['title_de', 'title_en']
        self.assertEqual(tuple(ma.get_form(request).base_fields.keys()), tuple(fields))
        self.assertEqual(
            tuple(ma.get_form(request, self.test_obj).base_fields.keys()), tuple(fields))

        # Using `fieldsets`.
        class TestModelAdmin(admin.TranslationAdmin):
            fieldsets = [(None, {'fields': ['title']})]

        ma = TestModelAdmin(models.TestModel, self.site)
        self.assertEqual(tuple(ma.get_form(request).base_fields.keys()), tuple(fields))
        self.assertEqual(
            tuple(ma.get_form(request, self.test_obj).base_fields.keys()), tuple(fields))

        # Using `exclude`.
        class TestModelAdmin(admin.TranslationAdmin):
            exclude = ['url', 'email']

        ma = TestModelAdmin(models.TestModel, self.site)
        fields = ['title_de', 'title_en', 'text_de', 'text_en']
        self.assertEqual(
            tuple(ma.get_form(request).base_fields.keys()), tuple(fields))

        # You can also pass a tuple to `exclude`.
        class TestModelAdmin(admin.TranslationAdmin):
            exclude = ('url', 'email')

        ma = TestModelAdmin(models.TestModel, self.site)
        self.assertEqual(
            tuple(ma.get_form(request).base_fields.keys()), tuple(fields))
        self.assertEqual(
            tuple(ma.get_form(request, self.test_obj).base_fields.keys()), tuple(fields))

        # Using `fields` and `exclude`.
        class TestModelAdmin(admin.TranslationAdmin):
            fields = ['title', 'url']
            exclude = ['url']

        ma = TestModelAdmin(models.TestModel, self.site)
        self.assertEqual(
            tuple(ma.get_form(request).base_fields.keys()), ('title_de', 'title_en'))

        # Using `fields` and `readonly_fields`.
        class TestModelAdmin(admin.TranslationAdmin):
            fields = ['title', 'url']
            readonly_fields = ['url']

        ma = TestModelAdmin(models.TestModel, self.site)
        self.assertEqual(
            tuple(ma.get_form(request).base_fields.keys()), ('title_de', 'title_en'))

        # Using `readonly_fields`.
        # Note: readonly fields are not included in the form.
        class TestModelAdmin(admin.TranslationAdmin):
            readonly_fields = ['title']

        ma = TestModelAdmin(models.TestModel, self.site)
        self.assertEqual(
            tuple(ma.get_form(request).base_fields.keys()),
            ('text_de', 'text_en', 'url_de', 'url_en', 'email_de', 'email_en'))

        # Using grouped fields.
        # Note: Current implementation flattens the nested fields.
        class TestModelAdmin(admin.TranslationAdmin):
            fields = (('title', 'url'), 'email',)

        ma = TestModelAdmin(models.TestModel, self.site)
        self.assertEqual(
            tuple(ma.get_form(request).base_fields.keys()),
            ('title_de', 'title_en', 'url_de', 'url_en', 'email_de', 'email_en'))

        # Using grouped fields in `fieldsets`.
        class TestModelAdmin(admin.TranslationAdmin):
            fieldsets = [(None, {'fields': ('email', ('title', 'url'))})]

        ma = TestModelAdmin(models.TestModel, self.site)
        fields = ['email_de', 'email_en', 'title_de', 'title_en', 'url_de', 'url_en']
        self.assertEqual(tuple(ma.get_form(request).base_fields.keys()), tuple(fields))
        self.assertEqual(
            tuple(ma.get_form(request, self.test_obj).base_fields.keys()), tuple(fields))

    def test_field_arguments_restricted_on_custom_form(self):
        # Using `fields`.
        class TestModelForm(forms.ModelForm):
            class Meta:
                model = models.TestModel
                fields = ['url', 'email']

        class TestModelAdmin(admin.TranslationAdmin):
            form = TestModelForm

        ma = TestModelAdmin(models.TestModel, self.site)
        fields = ['url_de', 'url_en', 'email_de', 'email_en']
        self.assertEqual(
            tuple(ma.get_form(request).base_fields.keys()), tuple(fields))
        self.assertEqual(
            tuple(ma.get_form(request, self.test_obj).base_fields.keys()), tuple(fields))

        # Using `exclude`.
        class TestModelForm(forms.ModelForm):
            class Meta:
                model = models.TestModel
                exclude = ['url', 'email']

        class TestModelAdmin(admin.TranslationAdmin):
            form = TestModelForm

        ma = TestModelAdmin(models.TestModel, self.site)
        fields = ['title_de', 'title_en', 'text_de', 'text_en']
        self.assertEqual(
            tuple(ma.get_form(request).base_fields.keys()), tuple(fields))
        self.assertEqual(
            tuple(ma.get_form(request, self.test_obj).base_fields.keys()), tuple(fields))

        # If both, the custom form an the ModelAdmin define an `exclude`
        # option, the ModelAdmin wins. This is Django behaviour.
        class TestModelAdmin(admin.TranslationAdmin):
            form = TestModelForm
            exclude = ['url']

        ma = TestModelAdmin(models.TestModel, self.site)
        fields = ['title_de', 'title_en', 'text_de', 'text_en', 'email_de',
                  'email_en']
        self.assertEqual(
            tuple(ma.get_form(request).base_fields.keys()), tuple(fields))
        self.assertEqual(
            tuple(ma.get_form(request, self.test_obj).base_fields.keys()), tuple(fields))

        # Same for `fields`.
        class TestModelForm(forms.ModelForm):
            class Meta:
                model = models.TestModel
                fields = ['text', 'title']

        class TestModelAdmin(admin.TranslationAdmin):
            form = TestModelForm
            fields = ['email']

        ma = TestModelAdmin(models.TestModel, self.site)
        fields = ['email_de', 'email_en']
        self.assertEqual(
            tuple(ma.get_form(request).base_fields.keys()), tuple(fields))
        self.assertEqual(
            tuple(ma.get_form(request, self.test_obj).base_fields.keys()), tuple(fields))

    def test_inline_fieldsets(self):
        class DataInline(admin.TranslationStackedInline):
            model = models.DataModel
            fieldsets = [
                ('Test', {'fields': ('data',)})
            ]

        class TestModelAdmin(admin.TranslationAdmin):
            exclude = ('title', 'text',)
            inlines = [DataInline]

        class DataTranslationOptions(translator.TranslationOptions):
            fields = ('data',)

        translator.translator.register(models.DataModel,
                                       DataTranslationOptions)
        ma = TestModelAdmin(models.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](
                models.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](
                models.TestModel, self.site).get_fieldsets(request, self.test_obj)
        self.assertEqual(ma_fieldsets, fieldsets)

        # Remove translation for DataModel
        translator.translator.unregister(models.DataModel)

    def test_list_editable(self):
        class TestModelAdmin(admin.TranslationAdmin):
            list_editable = ['title']
            list_display = ['id', 'title']
            list_display_links = ['id']

        ma = TestModelAdmin(models.TestModel, self.site)
        list_editable = ['title_de', 'title_en']
        list_display = ['id', 'title_de', 'title_en']
        self.assertEqual(tuple(ma.list_editable), tuple(list_editable))
        self.assertEqual(tuple(ma.list_display), tuple(list_display))

    def test_build_css_class(self):
        with reload_override_settings(LANGUAGES=(('de', 'German'), ('en', 'English'),
                                                 ('es-ar', 'Argentinian Spanish'),)):
            fields = {
                'foo_en': 'foo-en',
                'foo_es_ar': 'foo-es_ar',
                'foo_en_us': 'foo-en_us',
                'foo_bar_de': 'foo_bar-de',
                '_foo_en': '_foo-en',
                '_foo_es_ar': '_foo-es_ar',
                '_foo_bar_de': '_foo_bar-de',
                'foo__en': 'foo_-en',
                'foo__es_ar': 'foo_-es_ar',
                'foo_bar__de': 'foo_bar_-de',
            }
            for field, css in fields.items():
                self.assertEqual(build_css_class(field), css)

    def test_multitable_inheritance(self):
        class MultitableModelAAdmin(admin.TranslationAdmin):
            pass

        class MultitableModelBAdmin(admin.TranslationAdmin):
            pass

        maa = MultitableModelAAdmin(models.MultitableModelA, self.site)
        mab = MultitableModelBAdmin(models.MultitableModelB, self.site)

        self.assertEqual(tuple(maa.get_form(request).base_fields.keys()),
                         ('titlea_de', 'titlea_en'))
        self.assertEqual(tuple(mab.get_form(request).base_fields.keys()),
                         ('titlea_de', 'titlea_en', 'titleb_de', 'titleb_en'))

    def test_group_fieldsets(self):
        # Declared fieldsets take precedence over group_fieldsets
        class GroupFieldsetsModelAdmin(admin.TranslationAdmin):
            fieldsets = [(None, {'fields': ['title']})]
            group_fieldsets = True
        ma = GroupFieldsetsModelAdmin(models.GroupFieldsetsModel, self.site)
        fields = ['title_de', 'title_en']
        self.assertEqual(tuple(ma.get_form(request).base_fields.keys()), tuple(fields))
        self.assertEqual(
            tuple(ma.get_form(request, self.test_obj).base_fields.keys()), tuple(fields))

        # Now set group_fieldsets only
        class GroupFieldsetsModelAdmin(admin.TranslationAdmin):
            group_fieldsets = True
        ma = GroupFieldsetsModelAdmin(models.GroupFieldsetsModel, self.site)
        # Only text and title are registered for translation. We expect to get
        # three fieldsets. The first which gathers all untranslated field
        # (email only) and one for each translation field (text and title).
        fieldsets = [
            ('', {'fields': ['email']}),
            ('Title', {'classes': ('mt-fieldset',), 'fields': ['title_de', 'title_en']}),
            ('Text', {'classes': ('mt-fieldset',), 'fields': ['text_de', 'text_en']}),
        ]
        self.assertEqual(ma.get_fieldsets(request), fieldsets)
        self.assertEqual(ma.get_fieldsets(request, self.test_obj), fieldsets)

        # Verify that other options are still taken into account

        # Exclude an untranslated field
        class GroupFieldsetsModelAdmin(admin.TranslationAdmin):
            group_fieldsets = True
            exclude = ('email',)
        ma = GroupFieldsetsModelAdmin(models.GroupFieldsetsModel, self.site)
        fieldsets = [
            ('Title', {'classes': ('mt-fieldset',), 'fields': ['title_de', 'title_en']}),
            ('Text', {'classes': ('mt-fieldset',), 'fields': ['text_de', 'text_en']}),
        ]
        self.assertEqual(ma.get_fieldsets(request), fieldsets)
        self.assertEqual(ma.get_fieldsets(request, self.test_obj), fieldsets)

        # Exclude a translation field
        class GroupFieldsetsModelAdmin(admin.TranslationAdmin):
            group_fieldsets = True
            exclude = ('text',)
        ma = GroupFieldsetsModelAdmin(models.GroupFieldsetsModel, self.site)
        fieldsets = [
            ('', {'fields': ['email']}),
            ('Title', {'classes': ('mt-fieldset',), 'fields': ['title_de', 'title_en']})
        ]
        self.assertEqual(ma.get_fieldsets(request), fieldsets)
        self.assertEqual(ma.get_fieldsets(request, self.test_obj), fieldsets)

    def test_prepopulated_fields(self):
        trans_real.activate('de')
        self.assertEqual(get_language(), 'de')

        # Non-translated slug based on translated field (using active language)
        class NameModelAdmin(admin.TranslationAdmin):
            prepopulated_fields = {'slug': ('firstname',)}
        ma = NameModelAdmin(models.NameModel, self.site)
        self.assertEqual(ma.prepopulated_fields, {'slug': ('firstname_de',)})

        # Checking multi-field
        class NameModelAdmin(admin.TranslationAdmin):
            prepopulated_fields = {'slug': ('firstname', 'lastname',)}
        ma = NameModelAdmin(models.NameModel, self.site)
        self.assertEqual(ma.prepopulated_fields, {'slug': ('firstname_de', 'lastname_de',)})

        # Non-translated slug based on non-translated field (no change)
        class NameModelAdmin(admin.TranslationAdmin):
            prepopulated_fields = {'slug': ('age',)}
        ma = NameModelAdmin(models.NameModel, self.site)
        self.assertEqual(ma.prepopulated_fields, {'slug': ('age',)})

        # Translated slug based on non-translated field (all populated on the same value)
        class NameModelAdmin(admin.TranslationAdmin):
            prepopulated_fields = {'slug2': ('age',)}
        ma = NameModelAdmin(models.NameModel, self.site)
        self.assertEqual(ma.prepopulated_fields, {'slug2_en': ('age',), 'slug2_de': ('age',)})

        # Translated slug based on translated field (corresponding)
        class NameModelAdmin(admin.TranslationAdmin):
            prepopulated_fields = {'slug2': ('firstname',)}
        ma = NameModelAdmin(models.NameModel, self.site)
        self.assertEqual(ma.prepopulated_fields, {'slug2_en': ('firstname_en',),
                                                  'slug2_de': ('firstname_de',)})

        # Check that current active language is used
        trans_real.activate('en')
        self.assertEqual(get_language(), 'en')

        class NameModelAdmin(admin.TranslationAdmin):
            prepopulated_fields = {'slug': ('firstname',)}
        ma = NameModelAdmin(models.NameModel, self.site)
        self.assertEqual(ma.prepopulated_fields, {'slug': ('firstname_en',)})

        # Prepopulation language can be overriden by MODELTRANSLATION_PREPOPULATE_LANGUAGE
        with reload_override_settings(MODELTRANSLATION_PREPOPULATE_LANGUAGE='de'):
            class NameModelAdmin(admin.TranslationAdmin):
                prepopulated_fields = {'slug': ('firstname',)}
            ma = NameModelAdmin(models.NameModel, self.site)
            self.assertEqual(ma.prepopulated_fields, {'slug': ('firstname_de',)})

    def test_proxymodel_field_argument(self):
        class ProxyTestModelAdmin(admin.TranslationAdmin):
            fields = ['title']

        ma = ProxyTestModelAdmin(models.ProxyTestModel, self.site)
        fields = ['title_de', 'title_en']
        self.assertEqual(tuple(ma.get_form(request).base_fields.keys()), tuple(fields))
        self.assertEqual(
            tuple(ma.get_form(request, self.test_obj).base_fields.keys()), tuple(fields))


class ThirdPartyAppIntegrationTest(ModeltranslationTestBase):
    """
    This test case and a test case below have identical tests. The models they test have the same
    definition - but in this case the model is not registered for translation and in the other
    case it is.
    """
    registered = False

    @classmethod
    def setUpClass(cls):
        # 'model' attribute cannot be assigned to class in its definition,
        # because ``models`` module will be reloaded and hence class would use old model classes.
        super(ThirdPartyAppIntegrationTest, cls).setUpClass()
        cls.model = models.ThirdPartyModel

    def test_form(self):
        class CreationForm(forms.ModelForm):
            class Meta:
                model = self.model
                fields = '__all__'

        creation_form = CreationForm({'name': 'abc'})
        inst = creation_form.save()
        self.assertEqual('de', get_language())
        self.assertEqual('abc', inst.name)
        self.assertEqual(1, self.model.objects.count())


class ThirdPartyAppIntegrationRegisteredTest(ThirdPartyAppIntegrationTest):
    registered = True

    @classmethod
    def setUpClass(cls):
        super(ThirdPartyAppIntegrationRegisteredTest, cls).setUpClass()
        cls.model = models.ThirdPartyRegisteredModel


class TestManager(ModeltranslationTestBase):
    def setUp(self):
        # In this test case the default language is en, not de.
        super(TestManager, self).setUp()
        trans_real.activate('en')

    def test_filter_update(self):
        """Test if filtering and updating is language-aware."""
        n = models.ManagerTestModel(title='')
        n.title_en = 'en'
        n.title_de = 'de'
        n.save()

        m = models.ManagerTestModel(title='')
        m.title_en = 'title en'
        m.title_de = 'de'
        m.save()

        self.assertEqual('en', get_language())

        self.assertEqual(0, models.ManagerTestModel.objects.filter(title='de').count())
        self.assertEqual(1, models.ManagerTestModel.objects.filter(title='en').count())
        # Spanning works
        self.assertEqual(2, models.ManagerTestModel.objects.filter(title__contains='en').count())

        with override('de'):
            self.assertEqual(2, models.ManagerTestModel.objects.filter(title='de').count())
            self.assertEqual(0, models.ManagerTestModel.objects.filter(title='en').count())
            # Spanning works
            self.assertEqual(2, models.ManagerTestModel.objects.filter(title__endswith='e').count())

            # Still possible to use explicit language version
            self.assertEqual(1, models.ManagerTestModel.objects.filter(title_en='en').count())
            self.assertEqual(2, models.ManagerTestModel.objects.filter(
                             title_en__contains='en').count())

            models.ManagerTestModel.objects.update(title='new')
            self.assertEqual(2, models.ManagerTestModel.objects.filter(title='new').count())
            n = models.ManagerTestModel.objects.get(pk=n.pk)
            m = models.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)

        # Test Python3 "dictionary changed size during iteration"
        self.assertEqual(1, models.ManagerTestModel.objects.filter(title='en',
                                                                   title_en='en').count())

    def test_q(self):
        """Test if Q queries are rewritten."""
        n = models.ManagerTestModel(title='')
        n.title_en = 'en'
        n.title_de = 'de'
        n.save()

        self.assertEqual('en', get_language())
        self.assertEqual(0, models.ManagerTestModel.objects.filter(Q(title='de') |
                                                                   Q(pk=42)).count())
        self.assertEqual(1, models.ManagerTestModel.objects.filter(Q(title='en') |
                                                                   Q(pk=42)).count())

        with override('de'):
            self.assertEqual(1, models.ManagerTestModel.objects.filter(Q(title='de') |
                                                                       Q(pk=42)).count())
            self.assertEqual(0, models.ManagerTestModel.objects.filter(Q(title='en') |
                                                                       Q(pk=42)).count())

    def test_f(self):
        """Test if F queries are rewritten."""
        n = models.ManagerTestModel.objects.create(visits_en=1, visits_de=2)

        self.assertEqual('en', get_language())
        models.ManagerTestModel.objects.update(visits=F('visits') + 10)
        n = models.ManagerTestModel.objects.all()[0]
        self.assertEqual(n.visits_en, 11)
        self.assertEqual(n.visits_de, 2)

        with override('de'):
            models.ManagerTestModel.objects.update(visits=F('visits') + 20)
            n = models.ManagerTestModel.objects.all()[0]
            self.assertEqual(n.visits_en, 11)
            self.assertEqual(n.visits_de, 22)

    def test_order_by(self):
        """Check that field names are rewritten in order_by keys."""
        manager = models.ManagerTestModel.objects
        manager.create(title='a')
        m = manager.create(title='b')
        manager.create(title='c')
        with override('de'):
            # Make the order of the 'title' column different.
            m.title = 'd'
            m.save()
        titles_asc = tuple(m.title for m in manager.order_by('title'))
        titles_desc = tuple(m.title for m in manager.order_by('-title'))
        self.assertEqual(titles_asc, ('a', 'b', 'c'))
        self.assertEqual(titles_desc, ('c', 'b', 'a'))

    def test_order_by_meta(self):
        """Check that meta ordering is rewritten."""
        manager = models.ManagerTestModel.objects
        manager.create(title='more_de', visits_en=1, visits_de=2)
        manager.create(title='more_en', visits_en=2, visits_de=1)
        manager.create(title='most', visits_en=3, visits_de=3)
        manager.create(title='least', visits_en=0, visits_de=0)

        # Ordering descending with visits_en
        titles_for_en = tuple(m.title_en for m in manager.all())
        with override('de'):
            # Ordering descending with visits_de
            titles_for_de = tuple(m.title_en for m in manager.all())

        self.assertEqual(titles_for_en, ('most', 'more_en', 'more_de', 'least'))
        self.assertEqual(titles_for_de, ('most', 'more_de', 'more_en', 'least'))

    def assert_fallback(self, method, expected1, *args, **kwargs):
        transform = kwargs.pop('transform', lambda x: x)
        expected2 = kwargs.pop('expected_de', expected1)
        with default_fallback():
            # Fallback is ('de',)
            obj = method(*args, **kwargs)[0]
            with override('de'):
                obj2 = method(*args, **kwargs)[0]
        self.assertEqual(transform(obj), expected1)
        self.assertEqual(transform(obj2), expected2)

    def test_values_fallback(self):
        manager = models.ManagerTestModel.objects
        manager.create(title_en='', title_de='de')
        self.assertEqual('en', get_language())

        self.assert_fallback(manager.values, 'de', 'title', transform=lambda x: x['title'])
        self.assert_fallback(manager.values_list, 'de', 'title', flat=True)
        self.assert_fallback(manager.values_list, ('de', '', 'de'), 'title', 'title_en', 'title_de')

        # Settings are taken into account - fallback can be disabled
        with override_settings(MODELTRANSLATION_ENABLE_FALLBACKS=False):
            self.assert_fallback(manager.values, '', 'title', expected_de='de',
                                 transform=lambda x: x['title'])

        # Test fallback values
        manager = models.FallbackModel.objects
        manager.create()

        self.assert_fallback(manager.values, 'fallback', 'title', transform=lambda x: x['title'])
        self.assert_fallback(manager.values_list, ('fallback', 'fallback'), 'title', 'text')

    def test_values(self):
        manager = models.ManagerTestModel.objects
        id1 = manager.create(title_en='en', title_de='de').pk

        raw_obj = manager.raw_values('title')[0]
        obj = manager.values('title')[0]
        with override('de'):
            raw_obj2 = manager.raw_values('title')[0]
            obj2 = manager.values('title')[0]

        # Raw_values returns real database values regardless of current language
        self.assertEqual(raw_obj['title'], raw_obj2['title'])
        # Values present language-aware data, from the moment of retrieval
        self.assertEqual(obj['title'],  'en')
        self.assertEqual(obj2['title'], 'de')

        # Values_list behave similarly
        self.assertEqual(list(manager.values_list('title', flat=True)), ['en'])
        with override('de'):
            self.assertEqual(list(manager.values_list('title', flat=True)), ['de'])

        # One can always turn rewrite off
        a = list(manager.rewrite(False).values_list('title', flat=True))
        with override('de'):
            b = list(manager.rewrite(False).values_list('title', flat=True))
        self.assertEqual(a, b)

        i2 = manager.create(title_en='en2', title_de='de2')
        id2 = i2.pk

        # This is somehow repetitive...
        self.assertEqual('en', get_language())
        self.assertEqual(list(manager.values('title')), [{'title': 'en'}, {'title': 'en2'}])
        with override('de'):
            self.assertEqual(list(manager.values('title')), [{'title': 'de'}, {'title': 'de2'}])

        # When no fields are passed, list all fields in current language.
        self.assertEqual(list(manager.values()), [
            {'id': id1, 'title': 'en', 'visits': 0, 'description': None},
            {'id': id2, 'title': 'en2', 'visits': 0, 'description': None}
        ])
        # Similar for values_list
        self.assertEqual(list(manager.values_list()), [(id1, 'en', 0, None), (id2, 'en2', 0, None)])
        with override('de'):
            self.assertEqual(list(manager.values_list()),
                             [(id1, 'de', 0, None), (id2, 'de2', 0, None)])

        # Raw_values
        self.assertEqual(list(manager.raw_values()), list(manager.rewrite(False).values()))
        i2.delete()
        self.assertEqual(list(manager.raw_values()), [
            {'id': id1, 'title': 'en', 'title_en': 'en', 'title_de': 'de',
             'visits': 0, 'visits_en': 0, 'visits_de': 0,
             'description': None, 'description_en': None, 'description_de': None},
        ])

        # annotation issue (#374)
        self.assertEqual(list(manager.values_list('title', flat=True).annotate(Count('title'))),
                         ['en'])

    def test_values_list_annotation(self):
        models.TestModel(title='foo').save()
        models.TestModel(title='foo').save()
        self.assertEqual(
            list(models.TestModel.objects.all().values_list('title').annotate(Count('id'))),
            [('foo', 2)]
        )

    def test_custom_manager(self):
        """Test if user-defined manager is still working"""
        n = models.CustomManagerTestModel(title='')
        n.title_en = 'enigma'
        n.title_de = 'foo'
        n.save()

        m = models.CustomManagerTestModel(title='')
        m.title_en = 'enigma'
        m.title_de = 'bar'
        m.save()

        # Custom method
        self.assertEqual('bar', models.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, models.CustomManagerTestModel.objects.count())
        with override('de'):
            self.assertEqual(1, models.CustomManagerTestModel.objects.count())

    def test_custom_manager_custom_method_name(self):
        """Test if custom method also returns MultilingualQuerySet"""
        from modeltranslation.manager import MultilingualQuerySet
        qs = models.CustomManagerTestModel.objects.custom_qs()
        self.assertIsInstance(qs, MultilingualQuerySet)

    @skipUnless(MIGRATIONS, 'migrations/auth not available')
    def test_3rd_party_custom_manager(self):
        from django.contrib.auth.models import Group, GroupManager
        from modeltranslation.manager import MultilingualManager
        testmodel_fields = get_field_names(Group)
        self.assertIn('name',    testmodel_fields)
        self.assertIn('name_de', testmodel_fields)
        self.assertIn('name_en', testmodel_fields)
        self.assertIn('name_en', testmodel_fields)

        self.assertIsInstance(Group.objects, MultilingualManager)
        self.assertIsInstance(Group.objects, GroupManager)
        self.assertIn('get_by_natural_key', dir(Group.objects))

    def test_multilingual_queryset_pickling(self):
        import pickle
        from modeltranslation.manager import MultilingualQuerySet

        # typical
        models.CustomManagerTestModel.objects.create(title='a')
        qs = models.CustomManagerTestModel.objects.all()
        serialized = pickle.dumps(qs)
        deserialized = pickle.loads(serialized)
        self.assertIsInstance(deserialized, MultilingualQuerySet)
        self.assertListEqual(list(qs), list(deserialized))

        # Generated class
        models.CustomManager2TestModel.objects.create()
        qs = models.CustomManager2TestModel.objects.all()
        serialized = pickle.dumps(qs)
        deserialized = pickle.loads(serialized)
        self.assertIsInstance(deserialized, MultilingualQuerySet)
        self.assertIsInstance(deserialized, models.CustomQuerySet)
        self.assertListEqual(list(qs), list(deserialized))

    def test_non_objects_manager(self):
        """Test if managers other than ``objects`` are patched too"""
        from modeltranslation.manager import MultilingualManager
        manager = models.CustomManagerTestModel.another_mgr_name
        self.assertTrue(isinstance(manager, MultilingualManager))

    def test_custom_manager2(self):
        """Test if user-defined queryset is still working"""
        from modeltranslation.manager import MultilingualManager, MultilingualQuerySet
        manager = models.CustomManager2TestModel.objects
        self.assertTrue(isinstance(manager, models.CustomManager2))
        self.assertTrue(isinstance(manager, MultilingualManager))
        qs = manager.all()
        self.assertTrue(isinstance(qs, models.CustomQuerySet))
        self.assertTrue(isinstance(qs, MultilingualQuerySet))

    def test_creation(self):
        """Test if field are rewritten in create."""
        self.assertEqual('en', get_language())
        n = models.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 = models.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 = models.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 = models.ManagerTestModel.objects.populate(True).create(title='foo')
        self.assertEqual('foo', n.title_en)
        self.assertEqual('foo', n.title_de)
        self.assertEqual('foo', n.title)

        # You can specify some language...
        n = models.ManagerTestModel.objects.populate(True).create(title='foo', title_de='bar')
        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 = models.ManagerTestModel.objects.populate(True).create(title='foo', title_en='bar')
        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 method...
        n = models.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 reload_override_settings(MODELTRANSLATION_AUTO_POPULATE=True):
            self.assertEqual(True, mt_settings.AUTO_POPULATE)
            n = models.ManagerTestModel.objects.create(title='foo')
            self.assertEqual('foo', n.title_en)
            self.assertEqual('foo', n.title_de)
            self.assertEqual('foo', n.title)

            # populate method has highest priority
            n = models.ManagerTestModel.objects.populate(False).create(title='foo')
            self.assertEqual('foo', n.title_en)
            self.assertEqual(None, n.title_de)
            self.assertEqual('foo', n.title)

        # Populate ``default`` fills just the default translation.
        # TODO: Having more languages would make these tests more meaningful.
        qs = models.ManagerTestModel.objects
        m = qs.populate('default').create(title='foo', description='bar')
        self.assertEqual('foo', m.title_de)
        self.assertEqual('foo', m.title_en)
        self.assertEqual('bar', m.description_de)
        self.assertEqual('bar', m.description_en)
        with override('de'):
            m = qs.populate('default').create(title='foo', description='bar')
            self.assertEqual('foo', m.title_de)
            self.assertEqual(None, m.title_en)
            self.assertEqual('bar', m.description_de)
            self.assertEqual(None, m.description_en)

        # Populate ``required`` fills just non-nullable default translations.
        qs = models.ManagerTestModel.objects
        m = qs.populate('required').create(title='foo', description='bar')
        self.assertEqual('foo', m.title_de)
        self.assertEqual('foo', m.title_en)
        self.assertEqual(None, m.description_de)
        self.assertEqual('bar', m.description_en)
        with override('de'):
            m = qs.populate('required').create(title='foo', description='bar')
            self.assertEqual('foo', m.title_de)
            self.assertEqual(None, m.title_en)
            self.assertEqual('bar', m.description_de)
            self.assertEqual(None, m.description_en)

    def test_get_or_create_population(self):
        """
        Populate may be used with ``get_or_create``.
        """
        qs = models.ManagerTestModel.objects
        m1, created1 = qs.populate(True).get_or_create(title='aaa')
        m2, created2 = qs.populate(True).get_or_create(title='aaa')
        self.assertTrue(created1)
        self.assertFalse(created2)
        self.assertEqual(m1, m2)
        self.assertEqual('aaa', m1.title_en)
        self.assertEqual('aaa', m1.title_de)

    def test_fixture_population(self):
        """
        Test that a fixture with values only for the original fields
        does not result in missing default translations for (original)
        non-nullable fields.
        """
        with auto_populate('required'):
            call_command('loaddata', 'fixture.json', verbosity=0, commit=False)
            m = models.TestModel.objects.get()
            self.assertEqual(m.title_en, 'foo')
            self.assertEqual(m.title_de, 'foo')
            self.assertEqual(m.text_en, 'bar')
            self.assertEqual(m.text_de, None)

    def test_fixture_population_via_command(self):
        """
        Test that the loaddata command takes new option.
        """
        call_command('loaddata', 'fixture.json', verbosity=0, commit=False, populate='required')
        m = models.TestModel.objects.get()
        self.assertEqual(m.title_en, 'foo')
        self.assertEqual(m.title_de, 'foo')
        self.assertEqual(m.text_en, 'bar')
        self.assertEqual(m.text_de, None)

        call_command('loaddata', 'fixture.json', verbosity=0, commit=False, populate='all')
        m = models.TestModel.objects.get()
        self.assertEqual(m.title_en, 'foo')
        self.assertEqual(m.title_de, 'foo')
        self.assertEqual(m.text_en, 'bar')
        self.assertEqual(m.text_de, 'bar')

        # Test if option overrides current context
        with auto_populate('all'):
            call_command('loaddata', 'fixture.json', verbosity=0, commit=False, populate=False)
            m = models.TestModel.objects.get()
            self.assertEqual(m.title_en, 'foo')
            self.assertEqual(m.title_de, None)
            self.assertEqual(m.text_en, 'bar')
            self.assertEqual(m.text_de, None)

    def assertDeferred(self, use_defer, *fields):
        manager = models.TestModel.objects.defer if use_defer else models.TestModel.objects.only
        inst1 = manager(*fields)[0]
        with override('de'):
            inst2 = manager(*fields)[0]
        self.assertEqual('title_en', inst1.title)
        self.assertEqual('title_en', inst2.title)
        with override('de'):
            self.assertEqual('title_de', inst1.title)
            self.assertEqual('title_de', inst2.title)

    def assertDeferredClass(self, item):
        if NEW_DEFERRED_API:
            self.assertTrue(len(item.get_deferred_fields()) > 0)
        else:
            self.assertTrue(item.__class__._deferred)

    def test_deferred(self):
        """
        Check if ``only`` and ``defer`` are working.
        """
        models.TestModel.objects.create(title_de='title_de', title_en='title_en')
        inst = models.TestModel.objects.only('title_en')[0]
        if not NEW_DEFERRED_API:
            self.assertNotEqual(inst.__class__, models.TestModel)
        self.assertTrue(isinstance(inst, models.TestModel))
        self.assertDeferred(False, 'title_en')

        with auto_populate('all'):
            self.assertDeferred(False, 'title')
            self.assertDeferred(False, 'title_de')
            self.assertDeferred(False, 'title_en')
            self.assertDeferred(False, 'title_en', 'title_de')
            self.assertDeferred(False, 'title', 'title_en')
            self.assertDeferred(False, 'title', 'title_de')
            # Check if fields are deferred properly with ``only``
            self.assertDeferred(False, 'text')

            # Defer
            self.assertDeferred(True, 'title')
            self.assertDeferred(True, 'title_de')
            self.assertDeferred(True, 'title_en')
            self.assertDeferred(True, 'title_en', 'title_de')
            self.assertDeferred(True, 'title', 'title_en')
            self.assertDeferred(True, 'title', 'title_de')
            self.assertDeferred(True, 'text', 'email', 'url')

    def test_deferred_fk(self):
        """
        Check if ``select_related`` is rewritten and also
        if ``only`` and ``defer`` are working with deferred classes
        """
        test = models.TestModel.objects.create(title_de='title_de', title_en='title_en')
        with auto_populate('all'):
            models.ForeignKeyModel.objects.create(test=test)

        item = models.ForeignKeyModel.objects.select_related("test").defer("test__text")[0]
        self.assertDeferredClass(item.test)
        self.assertEqual('title_en', item.test.title)
        self.assertEqual('title_en', item.test.__class__.objects.only('title')[0].title)
        with override('de'):
            item = models.ForeignKeyModel.objects.select_related("test").defer("test__text")[0]
            self.assertDeferredClass(item.test)
            self.assertEqual('title_de', item.test.title)
            self.assertEqual('title_de', item.test.__class__.objects.only('title')[0].title)

    def test_deferred_spanning(self):
        test = models.TestModel.objects.create(title_de='title_de', title_en='title_en')
        with auto_populate('all'):
            models.ForeignKeyModel.objects.create(test=test)

        item1 = models.ForeignKeyModel.objects.select_related("test").defer("test__text")[0].test
        item2 = models.TestModel.objects.defer("text")[0]
        self.assertIs(item1.__class__, item2.__class__)
        # DeferredAttribute descriptors are present
        self.assertIn('text_en', dir(item1.__class__))
        self.assertIn('text_de', dir(item1.__class__))

    def test_deferred_rule2(self):
        models.TestModel.objects.create(title_de='title_de', title_en='title_en')
        o = models.TestModel.objects.only('title')[0]
        self.assertEqual(o.title, "title_en")
        o.title = "bla"
        self.assertEqual(o.title, "bla")

    def test_select_related(self):
        test = models.TestModel.objects.create(title_de='title_de', title_en='title_en')
        with auto_populate('all'):
            models.ForeignKeyModel.objects.create(untrans=test)

        fk_qs = models.ForeignKeyModel.objects.all()
        self.assertNotIn('_untrans_cache', fk_qs[0].__dict__)
        self.assertIn('_untrans_cache', fk_qs.select_related('untrans')[0].__dict__)
        self.assertNotIn(
            '_untrans_cache',
            fk_qs.select_related('untrans').select_related(None)[0].__dict__
        )
        # untrans is nullable so not included when select_related=True
        self.assertNotIn('_untrans_cache', fk_qs.select_related()[0].__dict__)

    def test_translation_fields_appending(self):
        from modeltranslation.manager import append_lookup_keys, append_lookup_key
        self.assertEqual(set(['untrans']), append_lookup_key(models.ForeignKeyModel, 'untrans'))
        self.assertEqual(set(['title', 'title_en', 'title_de']),
                         append_lookup_key(models.ForeignKeyModel, 'title'))
        self.assertEqual(set(['test', 'test_en', 'test_de']),
                         append_lookup_key(models.ForeignKeyModel, 'test'))
        self.assertEqual(set(['title__eq', 'title_en__eq', 'title_de__eq']),
                         append_lookup_key(models.ForeignKeyModel, 'title__eq'))
        self.assertEqual(set(['test__smt', 'test_en__smt', 'test_de__smt']),
                         append_lookup_key(models.ForeignKeyModel, 'test__smt'))
        big_set = set(['test__url', 'test__url_en', 'test__url_de',
                       'test_en__url', 'test_en__url_en', 'test_en__url_de',
                       'test_de__url', 'test_de__url_en', 'test_de__url_de'])
        self.assertEqual(big_set, append_lookup_key(models.ForeignKeyModel, 'test__url'))
        self.assertEqual(set(['untrans__url', 'untrans__url_en', 'untrans__url_de']),
                         append_lookup_key(models.ForeignKeyModel, 'untrans__url'))

        self.assertEqual(big_set.union(['title', 'title_en', 'title_de']),
                         append_lookup_keys(models.ForeignKeyModel, ['test__url', 'title']))

    def test_constructor_inheritance(self):
        inst = models.AbstractModelB()
        # Check if fields assigned in constructor hasn't been ignored.
        self.assertEqual(inst.titlea, 'title_a')
        self.assertEqual(inst.titleb, 'title_b')


class TranslationModelFormTest(ModeltranslationTestBase):
    def test_fields(self):
        class TestModelForm(TranslationModelForm):
            class Meta:
                model = models.TestModel
                fields = '__all__'

        form = TestModelForm()
        self.assertEqual(list(form.base_fields),
                         ['title', 'title_de', 'title_en', 'text', 'text_de', 'text_en',
                          'url', 'url_de', 'url_en', 'email', 'email_de', 'email_en'])
        self.assertEqual(list(form.fields), ['title', 'text', 'url', 'email'])

    def test_updating_with_empty_value(self):
        """
        Can we update the current language translation with an empty value, when
        the original field is excluded from the form?
        """
        class Form(forms.ModelForm):
            class Meta:
                model = models.TestModel
                exclude = ('text',)

        instance = models.TestModel.objects.create(text_de='something')
        form = Form({'text_de': '', 'title': 'a', 'email_de': '', 'email_en': ''},
                    instance=instance)
        instance = form.save()
        self.assertEqual('de', get_language())
        self.assertEqual('', instance.text_de)


class ProxyModelTest(ModeltranslationTestBase):
    def test_equality(self):
        n = models.TestModel.objects.create(title='Title')
        m = models.ProxyTestModel.objects.get(title='Title')
        self.assertEqual(n.title, m.title)
        self.assertEqual(n.title_de, m.title_de)
        self.assertEqual(n.title_en, m.title_en)


class TestRequired(ModeltranslationTestBase):
    def assertRequired(self, field_name):
        self.assertFalse(self.opts.get_field(field_name).blank)

    def assertNotRequired(self, field_name):
        self.assertTrue(self.opts.get_field(field_name).blank)

    def test_required(self):
        self.opts = models.RequiredModel._meta

        # All non required
        self.assertNotRequired('non_req')
        self.assertNotRequired('non_req_en')
        self.assertNotRequired('non_req_de')

        # Original required, but translated fields not - default behaviour
        self.assertRequired('req')
        self.assertNotRequired('req_en')
        self.assertNotRequired('req_de')

        # Set all translated field required
        self.assertRequired('req_reg')
        self.assertRequired('req_reg_en')
        self.assertRequired('req_reg_de')

        # Set some translated field required
        self.assertRequired('req_en_reg')
        self.assertRequired('req_en_reg_en')
        self.assertNotRequired('req_en_reg_de')

        # Test validation
        inst = models.RequiredModel()
        inst.req = 'abc'
        inst.req_reg = 'def'
        try:
            inst.full_clean()
        except ValidationError as e:
            error_fields = set(e.message_dict.keys())
            self.assertEqual(set(('req_reg_en', 'req_en_reg', 'req_en_reg_en')), error_fields)
        else:
            self.fail('ValidationError not raised!')


class M2MTest(ModeltranslationTestBase):
    def test_m2m(self):
        # Create 1 instance of Y, linked to 2 instance of X, with different
        # English and German names.
        x1 = models.ModelX.objects.create(name_en="foo", name_de="bar")
        x2 = models.ModelX.objects.create(name_en="bar", name_de="baz")
        y = models.ModelY.objects.create(title='y1')
        models.ModelXY.objects.create(model_x=x1, model_y=y)
        models.ModelXY.objects.create(model_x=x2, model_y=y)

        with override("en"):
            # There's 1 X named "foo" and it's x1
            y_foo = models.ModelY.objects.filter(xs__name="foo")
            self.assertEqual(1, y_foo.count())

            # There's 1 X named "bar" and it's x2 (in English)
            y_bar = models.ModelY.objects.filter(xs__name="bar")
            self.assertEqual(1, y_bar.count())

            # But in English, there's no X named "baz"
            y_baz = models.ModelY.objects.filter(xs__name="baz")
            self.assertEqual(0, y_baz.count())

            # Again: 1 X named "bar" (but through the M2M field)
            x_bar = y.xs.filter(name="bar")
            self.assertIn(x2, x_bar)
