Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allowing FORMAT_MODULE_PATH setting to be list of paths. #1236

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 23 additions & 15 deletions django/utils/formats.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -46,21 +46,29 @@ def iter_format_modules(lang, format_module_path=None):
""" """
Does the heavy lifting of finding format modules. Does the heavy lifting of finding format modules.
""" """
if check_for_language(lang): if not check_for_language(lang):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reversed the if-check at the beginning so that we can un-indent the whole code by one. Much nicer to read that way.

format_locations = ['django.conf.locale.%s'] return
if format_module_path:
format_locations.append(format_module_path + '.%s') if format_module_path is None:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Somehow, the parameter format_module_path was never used, only in that one failing test in i18n/tests.py - that test should have used with self.settings(FORMAT_MODULE_PATH='foobar') instead of passing in this parameter.

However, that parameter must be there for some reason and other people might use it in their codebases, so I changed the code slightly to honour the parameter again. If the parameter is given, we ignore the setting.

@timgraham this way the failing test passes. Do we need to document that format_module_path parameter and hint that it would override the FORMAT_MODULE_PATH setting when given? For people who mess around in undocumented internals? :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would check git blame to see if there's any indication of why format_module_path is there. I don't think we're under any obligation to keep it, as iter_format_modules isn't a documented function as you noted.

format_locations.reverse() format_module_path = settings.FORMAT_MODULE_PATH
locale = to_locale(lang)
locales = [locale] format_locations = []
if '_' in locale: if format_module_path:
locales.append(locale.split('_')[0]) if isinstance(format_module_path, six.string_types):
for location in format_locations: format_module_path = [format_module_path]
for loc in locales: for path in format_module_path:
try: format_locations.append(path + '.%s')
yield import_module('%s.formats' % (location % loc)) format_locations.append('django.conf.locale.%s')
except ImportError: locale = to_locale(lang)
pass 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): def get_format_modules(lang=None, reverse=False):
Expand Down
14 changes: 14 additions & 0 deletions docs/ref/settings.txt
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -1378,6 +1378,20 @@ like::
__init__.py __init__.py
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I described the new way to use this setting in the docs.

formats.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`, Available formats are :setting:`DATE_FORMAT`, :setting:`TIME_FORMAT`,
:setting:`DATETIME_FORMAT`, :setting:`YEAR_MONTH_FORMAT`, :setting:`DATETIME_FORMAT`, :setting:`YEAR_MONTH_FORMAT`,
:setting:`MONTH_DAY_FORMAT`, :setting:`SHORT_DATE_FORMAT`, :setting:`MONTH_DAY_FORMAT`, :setting:`SHORT_DATE_FORMAT`,
Expand Down
5 changes: 4 additions & 1 deletion docs/releases/1.8.txt
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -133,7 +133,10 @@ Forms
Internationalization 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 Management Commands
^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
Expand Down
17 changes: 13 additions & 4 deletions docs/topics/i18n/formatting.txt
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -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, 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. 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 Files are not placed directly in this directory, but in a directory named as
the locale, and must be named ``formats.py``. the locale, and must be named ``formats.py``.
Expand Down
Empty file added tests/i18n/other2/__init__.py
Empty file.
Empty file.
Empty file.
Empty file.
26 changes: 22 additions & 4 deletions tests/i18n/tests.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -737,12 +737,30 @@ def test_sanitize_separators(self):
def test_iter_format_modules(self): def test_iter_format_modules(self):
""" """
Tests the iter_format_modules function. Tests the iter_format_modules function.

Tests the case where FORMAT_MODULE_PATH is a string.
""" """
# 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): with translation.override('de-at', deactivate=True):
de_format_mod = import_module('django.conf.locale.de.formats') # Should return the correct default module when no setting is set
self.assertEqual(list(iter_format_modules('de')), [de_format_mod]) self.assertEqual(list(iter_format_modules('de')), [default_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]) # 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): def test_iter_format_modules_stability(self):
""" """
Expand Down