diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 6bad036..d09d219 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -4,16 +4,11 @@ v0.4.0-alpha1 CHANGED: Use app-level translation files in favour of a single project-level one. Adds an autoregister feature similiar to the one provided by Django's admin. A new setting MODELTRANSLATION_TRANSLATION_FILES keeps - backwards compatibility with older versions. This is basically a merge - from django-modeltranslation-wrapper with a few changes regarding how - registration is triggered. See documentation for details. - (thanks to Jacek Tomaszewski, the author of modeltranslation-wrapper, - resolves issues 58 and 71) - NOTE: If you are upgrading from an older version of modeltranslation - and upgrade directly from svn, make sure to delete a possible - models.pyc leftover in the modeltranslation directory. Otherwise you - might get a weird TypeError from Django trying to use the old file: - __import__() argument 1 must be string, not None. + backwards compatibility with older versions. See documentation for + details. This is basically a merge from both + django-modeltranslation-wrapper and hyperweek's branch at github. + (thanks to Jacek Tomaszewski, hyperweek and haineault) + resolves issues 19, 58 and 71) CHANGED: Moved tests to separate folder and added tests for TranslationAdmin. To run the tests the settings provided in model.tests.modeltranslation have to be used (settings.LANGUAGES override doesn't work for diff --git a/docs/modeltranslation/modeltranslation.txt b/docs/modeltranslation/modeltranslation.txt index 8e0285d..85cf4c8 100644 --- a/docs/modeltranslation/modeltranslation.txt +++ b/docs/modeltranslation/modeltranslation.txt @@ -49,9 +49,7 @@ in detail in the following sections: 4. Configure the ``MODELTRANSLATION_TRANSLATION_FILES`` variable in your ``settings.py``. -5. Add ``translator.autoregister()`` to your project's ``urls.py``. - -6. Sync the database using ``manage.py syncdb`` (note that this only applies +5. Sync the database using ``manage.py syncdb`` (note that this only applies if the models registered in the ``translations.py`` did not have been synced to the database before. If they did - read further down what to do in that case. @@ -212,27 +210,6 @@ translation will have been added some auto-magical fields. The next section explains how things are working under the hood. -Autoregister translation files -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Similiar to Django's admin, modeltranslation provides a mechanism to -automatically register your translation files. It has to be hooked into your -project's urls.py. - -:: - - from modeltranslation import translator - translator.autodiscover() - - # ... - # Must be in in front of: - - from django.contrib import admin - admin.autodiscover() - -.. note:: This step is basically optional if you prefer to register your models - programmatically. - - Changes automatically applied to the model class ------------------------------------------------ After registering the ``News`` model for transaltion an SQL dump of the diff --git a/modeltranslation/__init__.py b/modeltranslation/__init__.py index e69de29..5268f0b 100644 --- a/modeltranslation/__init__.py +++ b/modeltranslation/__init__.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- +# FIXME: autodiscover runs twice + +def autodiscover(): + """ + Auto-discover INSTALLED_APPS translation.py modules and fail silently when + not present. This forces an import on them to register. + Also import explicit modules. + """ + import sys + import copy + from django.conf import settings + from django.utils.importlib import import_module + from django.utils.module_loading import module_has_submodule + from modeltranslation.translator import translator + from modeltranslation.settings import TRANSLATION_FILES + + for app in settings.INSTALLED_APPS: + mod = import_module(app) + # Attempt to import the app's translation module. + module = '%s.translation' % app + before_import_registry = copy.copy(translator._registry) + try: + import_module(module) + except: + # Reset the model registry to the state before the last import as + # this import will have to reoccur on the next request and this + # could raise NotRegistered and AlreadyRegistered exceptions + translator._registry = before_import_registry + + # Decide whether to bubble up this error. If the app just + # doesn't have an translation module, we can ignore the error + # attempting to import it, otherwise we want it to bubble up. + if module_has_submodule(mod, 'translation'): + raise + + for module in TRANSLATION_FILES: + import_module(module) + + # In debug mode, print a list of registered models to stdout + if settings.DEBUG: + try: + if sys.argv[1] in ('runserver', 'runserver_plus'): + translated_model_names = ', '.join( + t.__name__ for t in translator._registry.keys()) + print('modeltranslation: Registered %d models for ' + 'translation (%s).' % (len(translator._registry), + translated_model_names)) + except IndexError: + pass + + +def handle_translation_registrations(*args, **kwargs): + """ + Ensures that any configuration of the TranslationOption(s) are handled when + importing modeltranslation. + + This makes it possible for scripts/management commands that affect models + but know nothing of modeltranslation. + """ + import inspect + from django.conf import settings + from modeltranslation.settings import ENABLE_REGISTRATIONS + + if not ENABLE_REGISTRATIONS: + # If the user really wants to disable this, they can, possibly at their + # own expense. This is generally only required in cases where other + # apps generate import errors and requires extra work on the user's + # part to make things work. + return + + # This is a little dirty but we need to run the code that follows only + # once, no matter how many times the main modeltranslation module is + # imported. We'll look through the stack to see if we appear anywhere and + # simply return if we do, allowing the original call to finish. + stack = inspect.stack() + + for stack_info in stack[1:]: + if 'handle_translation_registrations' in stack_info[3] \ + and __file__ == stack_info[2]: + return + + # Trigger autodiscover, causing any TranslationOption initialization + # code to execute. + autodiscover() + + +handle_translation_registrations() diff --git a/modeltranslation/settings.py b/modeltranslation/settings.py index 73e7c4f..dbbaa9b 100644 --- a/modeltranslation/settings.py +++ b/modeltranslation/settings.py @@ -34,3 +34,7 @@ try: settings, 'MODELTRANSLATION_CUSTOM_FIELDS', ('BooleanField',)) except IndexError: pass + +# Don't change this setting unless you really know what you are doing +ENABLE_REGISTRATIONS = getattr( + settings, 'MODELTRANSLATION_ENABLE_REGISTRATIONS', settings.USE_I18N) diff --git a/modeltranslation/translator.py b/modeltranslation/translator.py index 9cb934e..ca07008 100644 --- a/modeltranslation/translator.py +++ b/modeltranslation/translator.py @@ -232,47 +232,5 @@ class Translator(object): 'translation' % model.__name__) -def autodiscover(): - """ - Auto-discover INSTALLED_APPS translation.py modules and fail silently when - not present. This forces an import on them to register. - Also import explicit modules. - """ - import sys - from django.utils.importlib import import_module - from django.utils.module_loading import module_has_submodule - from modeltranslation.settings import TRANSLATION_FILES - - project_translations = TRANSLATION_FILES - - for app in settings.INSTALLED_APPS: - mod = import_module(app) - # Attempt to import the app's translation module. - module = '%s.translation' % app - try: - import_module(module) - except: - # Decide whether to bubble up this error. If the app just - # doesn't have an translation module, we can ignore the error - # attempting to import it, otherwise we want it to bubble up. - if module_has_submodule(mod, 'translation'): - raise - - for module in project_translations: - import_module(module) - - # In debug mode, print a list of registered models to stdout - if settings.DEBUG: - try: - if sys.argv[1] in ('runserver', 'runserver_plus'): - translated_model_names = ', '.join( - t.__name__ for t in translator._registry.keys()) - print('modeltranslation: Registered %d models for ' - 'translation (%s).' % (len(translator._registry), - translated_model_names)) - except IndexError: - pass - - # This global object represents the singleton translator object translator = Translator()