diff --git a/django/utils/formats.py b/django/utils/formats.py index fc68179f3a..521ab5c763 100644 --- a/django/utils/formats.py +++ b/django/utils/formats.py @@ -46,21 +46,29 @@ def iter_format_modules(lang, format_module_path=None): """ Does the heavy lifting of finding format modules. """ - if check_for_language(lang): - format_locations = ['django.conf.locale.%s'] - if format_module_path: - format_locations.append(format_module_path + '.%s') - format_locations.reverse() - locale = to_locale(lang) - locales = [locale] - if '_' in locale: - locales.append(locale.split('_')[0]) - for location in format_locations: - for loc in locales: - try: - yield import_module('%s.formats' % (location % loc)) - except ImportError: - pass + if not check_for_language(lang): + return + + if format_module_path is None: + format_module_path = settings.FORMAT_MODULE_PATH + + format_locations = [] + if format_module_path: + if isinstance(format_module_path, six.string_types): + format_module_path = [format_module_path] + for path in format_module_path: + format_locations.append(path + '.%s') + format_locations.append('django.conf.locale.%s') + locale = to_locale(lang) + locales = [locale] + if '_' in locale: + locales.append(locale.split('_')[0]) + for location in format_locations: + for loc in locales: + try: + yield import_module('%s.formats' % (location % loc)) + except ImportError: + pass def get_format_modules(lang=None, reverse=False): diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt index f82ced255b..acf42af7ad 100644 --- a/docs/ref/settings.txt +++ b/docs/ref/settings.txt @@ -1378,6 +1378,20 @@ like:: __init__.py formats.py +.. versionchanged:: 1.8 + + You can also set this setting to a list of Python paths, for example:: + + FORMAT_MODULE_PATH = [ + 'mysite.formats', + 'some_app.formats', + ] + + When Django searches for a certain format, it will go through all given + Python paths until it finds a module that actually defines the given + format. This means that formats defined in packages farther up in the list + will take precendence over the same formats in packages farther down. + Available formats are :setting:`DATE_FORMAT`, :setting:`TIME_FORMAT`, :setting:`DATETIME_FORMAT`, :setting:`YEAR_MONTH_FORMAT`, :setting:`MONTH_DAY_FORMAT`, :setting:`SHORT_DATE_FORMAT`, diff --git a/docs/releases/1.8.txt b/docs/releases/1.8.txt index 21864aa3c2..b0e3b035c0 100644 --- a/docs/releases/1.8.txt +++ b/docs/releases/1.8.txt @@ -139,7 +139,10 @@ Forms Internationalization ^^^^^^^^^^^^^^^^^^^^ -* ... +* :setting:`FORMAT_MODULE_PATH` can now be a list of strings representing + module paths. This allows importing several format modules from different + reusable apps. It also allows overriding those custom formats in your main + Django project. Management Commands ^^^^^^^^^^^^^^^^^^^ diff --git a/docs/topics/i18n/formatting.txt b/docs/topics/i18n/formatting.txt index 04b36e2767..e726ddc808 100644 --- a/docs/topics/i18n/formatting.txt +++ b/docs/topics/i18n/formatting.txt @@ -154,11 +154,20 @@ Django provides format definitions for many locales, but sometimes you might want to create your own, because a format files doesn't exist for your locale, or because you want to overwrite some of the values. -To use custom formats, specify the path where you'll place format files first. -To do that, just set your :setting:`FORMAT_MODULE_PATH` setting to the package -where format files will exist, for instance:: - FORMAT_MODULE_PATH = 'mysite.formats' +.. versionchanged:: 1.8 + + The ability to specify :setting:`FORMAT_MODULE_PATH` as a list was added. + Previously, only a single string value was supported. + +To use custom formats, specify the path where you'll place format files +first. To do that, just set your :setting:`FORMAT_MODULE_PATH` setting to +the package where format files will exist, for instance:: + + FORMAT_MODULE_PATH = [ + 'mysite.formats', + 'some_app.formats', + ] Files are not placed directly in this directory, but in a directory named as the locale, and must be named ``formats.py``. diff --git a/tests/i18n/other2/__init__.py b/tests/i18n/other2/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/i18n/other2/locale/__init__.py b/tests/i18n/other2/locale/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/i18n/other2/locale/de/__init__.py b/tests/i18n/other2/locale/de/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/i18n/other2/locale/de/formats.py b/tests/i18n/other2/locale/de/formats.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/i18n/tests.py b/tests/i18n/tests.py index 8d0fc963af..d07c0d94a0 100644 --- a/tests/i18n/tests.py +++ b/tests/i18n/tests.py @@ -738,11 +738,27 @@ class FormattingTests(TestCase): """ Tests the iter_format_modules function. """ + # Importing some format modules so that we can compare the returned + # modules with these expected modules + default_mod = import_module('django.conf.locale.de.formats') + test_mod = import_module('i18n.other.locale.de.formats') + test_mod2 = import_module('i18n.other2.locale.de.formats') + with translation.override('de-at', deactivate=True): - de_format_mod = import_module('django.conf.locale.de.formats') - self.assertEqual(list(iter_format_modules('de')), [de_format_mod]) - test_de_format_mod = import_module('i18n.other.locale.de.formats') - self.assertEqual(list(iter_format_modules('de', 'i18n.other.locale')), [test_de_format_mod, de_format_mod]) + # Should return the correct default module when no setting is set + self.assertEqual(list(iter_format_modules('de')), [default_mod]) + + # When the setting is a string, should return the given module and + # the default module + self.assertEqual( + list(iter_format_modules('de', 'i18n.other.locale')), + [test_mod, default_mod]) + + # When setting is a list of strings, should return the given + # modules and the default module + self.assertEqual( + list(iter_format_modules('de', ['i18n.other.locale', 'i18n.other2.locale'])), + [test_mod, test_mod2, default_mod]) def test_iter_format_modules_stability(self): """