How do I output HTML in a message in the new Django messages framework?

Django

Django Problem Overview


I'm trying to display a bit of html in a message that's being displayed via the new Django messages framework. Specifically, I'm doing this via the ModelAdmin.message_user method, which is just a thin wrapper around messages():

def message_user(self, request, message):
    """
    Send a message to the user. The default implementation
    posts a message using the django.contrib.messages backend.
    """
    messages.info(request, message)

Everything I've tried so far seems to display escaped HTML.

self.message_user(request, "<a href=\"http://www.google.com\">Here's google!</a>")

Doesn't work, nor does:

from django.utils.safestring import mark_safe
...
self.message_user(request, mark_safe("<a href=\"http://www.google.com\">Here's google!</a>"))

The display of the template code in the admin base.html template is pretty straightforward:

    {% if messages %}
    <ul class="messagelist">{% for message in messages %}<li>{{ message }}</li>{% endfor %}</ul>
    {% endif %}

So I'm not exactly sure what I am doing wrong.

Thoughts or guidance greatly appreciated, thanks!

Django Solutions


Solution 1 - Django

Another option is to use extra_tags keyword arg to indicate that a message is safe. Eg

messages.error(request, 'Here is a <a href="/">link</a>', extra_tags='safe')

then use template logic to use the safe filter

{% for message in messages %}
    <li class="{{ message.tags }}">
    {% if 'safe' in message.tags %}{{ message|safe }}{% else %}{{ message }}{% endif %}
    </li>
{% endfor %}

Solution 2 - Django

As noted in the following Django ticket, it should work if you use mark_safe() in combination with the SessionStorage backend: https://code.djangoproject.com/ticket/14976#comment:9

Solution 3 - Django

This worked for me (Django 1.11):

from django.contrib import messages
from django.utils.safestring import mark_safe

messages.info(request, mark_safe('This is link to <a href="http://google.com">http://google.com</a>'))

Solution 4 - Django

Have you tried {{ message | safe }}?

In the Django template system template variables are always escaped, unless you specify them as safe with the safe filter. This default makes even the unaware protected against an injection attack.

I'm not sure how that interacts with mark_safe, but perhaps something happened in between that made it unsafe again.

Solution 5 - Django

You can use format_html. It applies escaping to all arguments.

For example, if we can link with a 'mymodel' detail using an attribute call 'name':

from django.contrib import messages
from django.utils.html import format_html


message = format_html("{} <a href='{}'>{}</a>",
                      "This is the mymodel", 
                      reverse('myapp:mymodel-detail', args=(mymodel.id,)),
                      mymodel.name)
messages.info(request, message)

This answer is based on https://stackoverflow.com/a/33751717/3816639

Solution 6 - Django

As Ryan Kaske said here, the correct way is to use {{ message.message }} instead of {{ message }}. e.g.

{% if messages %}
    <ul class="messagelist">
        {% for message in messages %}
            <li>{{ message.message }}</li>
        {% endfor %}
    </ul>
{% endif %}

Solution 7 - Django

The entire point of the templating system is to deal with strings and data like this.

While every other answer instructs you to mark your built string as safe, I would go one step further and tell you to never use HTML in your code - always use a template instead.

The template system makes sure things are properly escaped so you don't have to worry about it, and it's much harder for the programmer to get into the situation where they're building up an HTML string out of a bunch of ifs, and user data.

app/templates/app/fragments/google_link.html:

<a href="https://www.google.com">Here's Google!</a>

views.py:

from django.template import loader

...

def view(request):
    messages.info(
        request,
        loader.render_to_string(
            'app/fragments/google_link.html',
            {},
            request=request,
        ),
    )

Solution 8 - Django

I was looking for a way to use unescaped HTML in an admin listing. Not sure if this applies to the messages framework, but using allow_tags as described here helped me.

http://urlencode.blogspot.com/2009/10/neat-django-admin-tricks-part-1.html

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionjsdaltonView Question on Stackoverflow
Solution 1 - DjangoDavidWinterbottomView Answer on Stackoverflow
Solution 2 - DjangojphalipView Answer on Stackoverflow
Solution 3 - DjangovalexView Answer on Stackoverflow
Solution 4 - DjangoDanny RobertsView Answer on Stackoverflow
Solution 5 - DjangonegasView Answer on Stackoverflow
Solution 6 - DjangoTaha JahangirView Answer on Stackoverflow
Solution 7 - DjangoAdam BarnesView Answer on Stackoverflow
Solution 8 - DjangoLarryView Answer on Stackoverflow