Fixed #27067 -- Deprecated string_concat() in favor of format_lazy().

This commit is contained in:
Mattias Loverot 2016-08-16 12:07:03 +02:00 committed by Tim Graham
parent 13c3e5d5a0
commit 2315114090
10 changed files with 44 additions and 20 deletions

View File

@ -507,6 +507,7 @@ answer newbie questions, and generally made Django that much better:
Matt Riggott Matt Riggott
Matt Robenolt <m@robenolt.com> Matt Robenolt <m@robenolt.com>
Mattia Larentis <mattia@laretis.eu> Mattia Larentis <mattia@laretis.eu>
Mattias Loverot <mattias@stubin.se>
mattycakes@gmail.com mattycakes@gmail.com
Max Burstein <http://maxburstein.com> Max Burstein <http://maxburstein.com>
Max Derkachev <mderk@yandex.ru> Max Derkachev <mderk@yandex.ru>

View File

@ -43,9 +43,9 @@ from django.utils.encoding import force_text, python_2_unicode_compatible
from django.utils.html import escape, format_html from django.utils.html import escape, format_html
from django.utils.http import urlencode, urlquote from django.utils.http import urlencode, urlquote
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django.utils.text import capfirst, get_text_list from django.utils.text import capfirst, format_lazy, get_text_list
from django.utils.translation import ( from django.utils.translation import (
override as translation_override, string_concat, ugettext as _, ungettext, override as translation_override, ugettext as _, ungettext,
) )
from django.views.decorators.csrf import csrf_protect from django.views.decorators.csrf import csrf_protect
from django.views.generic import RedirectView from django.views.generic import RedirectView
@ -258,7 +258,7 @@ class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)):
if isinstance(form_field.widget, SelectMultiple) and not isinstance(form_field.widget, CheckboxSelectMultiple): if isinstance(form_field.widget, SelectMultiple) and not isinstance(form_field.widget, CheckboxSelectMultiple):
msg = _('Hold down "Control", or "Command" on a Mac, to select more than one.') msg = _('Hold down "Control", or "Command" on a Mac, to select more than one.')
help_text = form_field.help_text help_text = form_field.help_text
form_field.help_text = string_concat(help_text, ' ', msg) if help_text else msg form_field.help_text = format_lazy('{} {}', help_text, msg) if help_text else msg
return form_field return form_field
def get_view_on_site_url(self, obj=None): def get_view_on_site_url(self, obj=None):

View File

@ -2,7 +2,7 @@ from __future__ import unicode_literals
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.utils.functional import SimpleLazyObject from django.utils.functional import SimpleLazyObject
from django.utils.translation import string_concat from django.utils.text import format_lazy
def prefix_validation_error(error, prefix, code, params): def prefix_validation_error(error, prefix, code, params):
@ -15,10 +15,11 @@ def prefix_validation_error(error, prefix, code, params):
return ValidationError( return ValidationError(
# We can't simply concatenate messages since they might require # We can't simply concatenate messages since they might require
# their associated parameters to be expressed correctly which # their associated parameters to be expressed correctly which
# is not something `string_concat` does. For example, proxied # is not something `format_lazy` does. For example, proxied
# ungettext calls require a count parameter and are converted # ungettext calls require a count parameter and are converted
# to an empty string if they are missing it. # to an empty string if they are missing it.
message=string_concat( message=format_lazy(
'{}{}',
SimpleLazyObject(lambda: prefix % params), SimpleLazyObject(lambda: prefix % params),
SimpleLazyObject(lambda: error.message % error_params), SimpleLazyObject(lambda: error.message % error_params),
), ),

View File

@ -22,8 +22,8 @@ from django.utils.encoding import (
force_text, python_2_unicode_compatible, smart_text, force_text, python_2_unicode_compatible, smart_text,
) )
from django.utils.functional import cached_property from django.utils.functional import cached_property
from django.utils.text import camel_case_to_spaces from django.utils.text import camel_case_to_spaces, format_lazy
from django.utils.translation import override, string_concat from django.utils.translation import override
NOT_PROVIDED = object() NOT_PROVIDED = object()
@ -197,7 +197,7 @@ class Options(object):
# verbose_name_plural is a special case because it uses a 's' # verbose_name_plural is a special case because it uses a 's'
# by default. # by default.
if self.verbose_name_plural is None: if self.verbose_name_plural is None:
self.verbose_name_plural = string_concat(self.verbose_name, 's') self.verbose_name_plural = format_lazy('{}s', self.verbose_name)
# order_with_respect_and ordering are mutually exclusive. # order_with_respect_and ordering are mutually exclusive.
self._ordering_clash = bool(self.ordering and self.order_with_respect_to) self._ordering_clash = bool(self.ordering and self.order_with_respect_to)
@ -206,7 +206,7 @@ class Options(object):
if meta_attrs != {}: if meta_attrs != {}:
raise TypeError("'class Meta' got invalid attribute(s): %s" % ','.join(meta_attrs.keys())) raise TypeError("'class Meta' got invalid attribute(s): %s" % ','.join(meta_attrs.keys()))
else: else:
self.verbose_name_plural = string_concat(self.verbose_name, 's') self.verbose_name_plural = format_lazy('{}s', self.verbose_name)
del self.meta del self.meta
# If the db_table wasn't provided, use the app_label + model_name. # If the db_table wasn't provided, use the app_label + model_name.

View File

@ -4,9 +4,11 @@ Internationalization support.
from __future__ import unicode_literals from __future__ import unicode_literals
import re import re
import warnings
from django.utils import six from django.utils import six
from django.utils.decorators import ContextDecorator from django.utils.decorators import ContextDecorator
from django.utils.deprecation import RemovedInDjango21Warning
from django.utils.encoding import force_text from django.utils.encoding import force_text
from django.utils.functional import lazy from django.utils.functional import lazy
@ -224,6 +226,10 @@ def _string_concat(*strings):
Lazy variant of string concatenation, needed for translations that are Lazy variant of string concatenation, needed for translations that are
constructed from multiple parts. constructed from multiple parts.
""" """
warnings.warn(
'django.utils.translate.string_concat() is deprecated in '
'favor of django.utils.text.format_lazy().',
RemovedInDjango21Warning, stacklevel=2)
return ''.join(force_text(s) for s in strings) return ''.join(force_text(s) for s in strings)
string_concat = lazy(_string_concat, six.text_type) string_concat = lazy(_string_concat, six.text_type)

View File

@ -25,6 +25,8 @@ details on these changes.
* ``django.test.runner.setup_databases()`` will be removed. * ``django.test.runner.setup_databases()`` will be removed.
* ``django.utils.translation.string_concat()`` will be removed.
.. _deprecation-removed-in-2.0: .. _deprecation-removed-in-2.0:
2.0 2.0

View File

@ -1107,6 +1107,12 @@ For a complete discussion on the usage of the following see the
.. function:: string_concat(*strings) .. function:: string_concat(*strings)
.. deprecated:: 1.11
Use :meth:`django.utils.text.format_lazy` instead.
``string_concat(*strings)`` can be replaced by
``format_lazy('{}' * len(strings), *strings)``.
Lazy variant of string concatenation, needed for translations that are Lazy variant of string concatenation, needed for translations that are
constructed from multiple parts. constructed from multiple parts.

View File

@ -477,3 +477,7 @@ Miscellaneous
* ``django.test.runner.setup_databases()`` is moved to * ``django.test.runner.setup_databases()`` is moved to
:func:`django.test.utils.setup_databases`. The old location is deprecated. :func:`django.test.utils.setup_databases`. The old location is deprecated.
* ``django.utils.translation.string_concat()`` is deprecated in
favor of :func:`django.utils.text.format_lazy`. ``string_concat(*strings)``
can be replaced by ``format_lazy('{}' * len(strings), *strings)``.

View File

@ -489,21 +489,22 @@ directly with the ``number`` argument::
raise forms.ValidationError(self.error_message % number) raise forms.ValidationError(self.error_message % number)
Joining strings: ``string_concat()`` Formatting strings: ``format_lazy()``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Standard Python string joins (``''.join([...])``) will not work on lists Python's :meth:`str.format()` method will not work when either the
containing lazy translation objects. Instead, you can use ``format_string`` or any of the arguments to :meth:`str.format()`
:func:`django.utils.translation.string_concat()`, which creates a lazy object contains lazy translation objects. Instead, you can use
that concatenates its contents *and* converts them to strings only when the :func:`django.utils.text.format_lazy()`, which creates a lazy object
result is included in a string. For example:: that runs the ``str.format()`` method only when the result is included
in a string. For example::
from django.utils.translation import string_concat from django.utils.text import format_lazy
from django.utils.translation import ugettext_lazy from django.utils.translation import ugettext_lazy
... ...
name = ugettext_lazy('John Lennon') name = ugettext_lazy('John Lennon')
instrument = ugettext_lazy('guitar') instrument = ugettext_lazy('guitar')
result = string_concat(name, ': ', instrument) result = format_lazy('{name}: {instrument}', name=name, instrument=instrument)
In this case, the lazy translations in ``result`` will only be converted to In this case, the lazy translations in ``result`` will only be converted to
strings when ``result`` itself is used in a string (usually at template strings when ``result`` itself is used in a string (usually at template

View File

@ -16,10 +16,12 @@ from django.conf import settings
from django.conf.urls.i18n import i18n_patterns from django.conf.urls.i18n import i18n_patterns
from django.template import Context, Template, TemplateSyntaxError from django.template import Context, Template, TemplateSyntaxError
from django.test import ( from django.test import (
RequestFactory, SimpleTestCase, TestCase, override_settings, RequestFactory, SimpleTestCase, TestCase, ignore_warnings,
override_settings,
) )
from django.utils import six, translation from django.utils import six, translation
from django.utils._os import upath from django.utils._os import upath
from django.utils.deprecation import RemovedInDjango21Warning
from django.utils.formats import ( from django.utils.formats import (
date_format, get_format, get_format_modules, iter_format_modules, localize, date_format, get_format, get_format_modules, iter_format_modules, localize,
localize_input, reset_format_cache, sanitize_separators, time_format, localize_input, reset_format_cache, sanitize_separators, time_format,
@ -417,6 +419,7 @@ class TranslationTests(SimpleTestCase):
' super results{% endblocktrans %}' ' super results{% endblocktrans %}'
) )
@ignore_warnings(category=RemovedInDjango21Warning)
def test_string_concat(self): def test_string_concat(self):
""" """
six.text_type(string_concat(...)) should not raise a TypeError - #4796 six.text_type(string_concat(...)) should not raise a TypeError - #4796