I want to insert a resolved URL into a string, used as the help_text
of a form field:
class ContactForm(forms.Form):
[...]
email_sender = forms.EmailField(
label="Votre email",
widget=forms.EmailInput(attrs={'disabled': 'disabled'}),
help_text="[...], <a href='{}'>[...]</a>.".format(reverse_lazy('account_email'))
)
But inserting the reversed URL into the string is not possible, because the format
function (or any concatenation way I tried) is not "lazy", and wants to produce the output immediately.
I get the following error:
django.core.exceptions.ImproperlyConfigured: The included urlconf 'myproject.urls' does not appear to have any patterns in it. If you see valid patterns in the file then the issue is probably caused by a circular import.
For example, using the following code works perfectly, but is not what I want :)
email_sender = forms.EmailField(
help_text=reverse_lazy('account_email')
)
So how do I concatenate a string of "lazy" values?
Note: This was written in the times of Django 2. While the essence is still valid as of Django 4, the helper function described in this post is now provided out-of-the-box as format_lazy
.
You cannot concatenate lazy strings in Django. The implementation is very basic, it's not even actual lazy strings, it's lazy function calls, they may return other types. When you do reverse_lazy
, you do just get a lazy function call with no special other behavior
So, just play by the rules. If you need a string to be lazily computed, create a lazy function yourself:
from django.utils.functional import lazy
def email_sender_help_text():
return "[...], <a href='{}'>[...]</a>.".format(reverse('account_email'))
email_sender_help_text_lazy = lazy(email_sender_help_text, str)
You can now use it:
email_sender = forms.EmailField(
help_text=email_sender_help_text_lazy()
)
Or, for a more general version:
from django.utils.functional import lazy
def format(string, *args, **kwargs):
return string.format(*args, **kwargs)
lazy_format = lazy(format, str)
help_text=lazy_format("<a href='{}'>", reverse_lazy('account_email'))
You may also want to check out django.utils.functional.allow_lazy
.
Later edit:
Since Django 3, Django provides exactly that helper function, as format_lazy
. So use that instead of recreating it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With