20

I'm creating a web app using Flask to deal with GoogleOpenID, these codes are almost finished, except the flashing message contains a link:

@oid.after_login
def create_or_login(resp):
    user = db_session.query(User).filter_by(email=resp.email).first()
    if user is not None:
        flash('Successfully signed in', 'success')
    else:
        user = User(nickname=resp.fullname, source=GOOGLE, email=resp.email)
        db_session.add(user)
        db_session.commit()
        flash(flashing_message, 'success')
    g.user = user
    session['nickname'] = user.nickname
    return redirect(oid.get_next_url())

It works well when flashing_message is like this: 'Successfully registered, please click here'

But when flashing_message is 'Successfully registered, please click <a href="/me" class="alert-link">here</a>', it doesn't work (flashes nothing) without throwing any Error. Strangely, sentences between flash() and return doesn't work either (did not set session['nickname] or g.user).

Kane Blueriver
  • 4,170
  • 4
  • 29
  • 48

4 Answers4

49

The other answers here focus on changing your template to allow all flash messages to be marked as safe, which may not be what you want.

If you just want to mark certain flashed messages as safe, wrap the text passed to flash() in Markup(). (Flask API Docs for Markup)

For example, instead of:

flash('Successfully registered, please click <a href="/me" class="alert-link">here</a>')

Wrap the string in Markup() like this:

flash(Markup('Successfully registered, please click <a href="/me" class="alert-link">here</a>'))

As always, you will need to import Markup from the flask package something like:

from flask import Markup
Pete Watts
  • 1,014
  • 10
  • 10
3

You need to render a template after calling flash() which should then get the message using get_flashed_messages(). You need to adjust your code to call a template after calling flash(). flash sends the message to next request which can be extracted by the template. The template can look something like:

{% with messages = get_flashed_messages() %}
    {% if messages %}
    <ul class=flashes>
    {% for message in messages %}
        <li>{{ message | safe }}</li>
    {% endfor %}
    </ul>
  {% endif %}
{% endwith %}

In your view code, I would add a render_template right after the flash() call. Something like:

flash('success')
return render_template('whatever.html')   
codegeek
  • 32,236
  • 12
  • 63
  • 63
  • Yes, I know how to use flash(), it's all right when flashing_message is a normal string, but when it's 'Successfully registered, please click here', something went wrong. – Kane Blueriver Jan 24 '14 at 03:08
2

Escaping HTML is the default behavior, so pass it through the safe filter to display the HTML unescaped:

{{ message|safe }}
Blender
  • 289,723
  • 53
  • 439
  • 496
  • @kxxoling: What error are you getting? You need to be more descriptive. – Blender Jan 21 '14 at 04:29
  • 'cause it doesn't throw any Error message, I'm very confused too. But I found if there's html tag in flashing message, it seems don't work to set session or g.user. – Kane Blueriver Jan 21 '14 at 04:35
  • @kxxoling: How do you know that it doesn't work? Are you running the development server with `debug=True`? – Blender Jan 21 '14 at 04:36
  • Yes, debug mode is true. In this case it redirects to /auth, but if 'nickname' in session or g.user resist, page would redirect to / as expected. I also tried to change the upper flashing_message to test, finally I find it must be something wrong with html in flash(), but safe filter doesn't work. – Kane Blueriver Jan 21 '14 at 04:48
0

I am not sure if it is correct, but the way I solved this was by declaring another variable in the function that was a string of HTML and then passing it through a render_template() function.

And in the template passed it through the safe filter.

For example, (roughly based on) the code you have provided:

@oid.after_login
def create_or_login(resp):
    user = db_session.query(User).filter_by(email=resp.email).first()
    if user is not None:
        flash('Successfully signed in', 'success')
    else:
        user = User(nickname=resp.fullname, source=GOOGLE, email=resp.email)
        db_session.add(user)
        db_session.commit()
        flash(flashing_message, 'success')
        link = "<a href=\"some_link.html\">Link</a>" ## ADDED HTML WITH LINK ##
    g.user = user
    session['nickname'] = user.nickname
    return render_template('some_layout.html',
                           link=link  ## PASS THE LINK TO THE TEMPLATE ##
    )

Then in the template I added an extra if statement inside the get_flashed_messages() if:

{% with messages = get_flashed_messages(with_categories=true) %}
    {% if messages %}

    ... Code that was already here ...

    {% if link %}        ## PASSED THE LINK TO THE TEMPLATE HERE ##
      {{ link | safe }}
    {% endif %}

    {% endif %}
{% endwith %}
m__
  • 101
  • 3