最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Flask flash and url_for with AJAX - Stack Overflow

programmeradmin2浏览0评论

I am currently stuck on a rather "big" problem, and I will try to be as clear and concise as I can about it. I am developing a tool in Python, using Flask. It's meant to be an intranet. The idea is that I have a client page. There's its name, numerous other infos, and a massive checklist.

The infos are already written in input fields, so that the user just has to edit them there, press enter, and the page is reloaded with the edited information (with a notification, using the flashed messages and growl).

The massive checklist is forcing me to move to a system using AJAX, since I want it to be updated "live" as users tick boxes and enter content in inputs, without reloading the page.

As a consequence, I'm also switching my basic inputs for infos to AJAX, to avoid the reloading of the page.

I've come across two issues :

  1. The first one concerns the message flashing feature of Flask.

    I've got a method that updates a client name in the database, flashes a message (success or failure, depending on numerous things), then displays the page again. To avoid the page reload, I'm managing the form submit with AJAX.

    The problem is, my python method will flash messages. And once I'm back on my html page (that has not been reloaded, since it's AJAX), the get_flashed_message function of Jinja2 returns me nothing, since it has not been updated. As a consequence, it's impossible for me to retrieve those flashed messages.

    How can I get these? The only solution I see is getting rid of any usage of flash, and coding my own messages system that I would return from the method, and then treat in javascript. This seems incredibly dumb, since flash is a feature of Flask, and it's made "useless" by AJAX. I feel like I'm missing something.

    (As a note, until now, my flashed messages management is made in my base layout, that calls a template that runs through all messages and displays them as growl notifications)

  2. Second issue concerns url_for. Here is my form, for editing the client's name :

<form id="changeClientName" method="POST" action="{{ url_for('clients.edit_client_name', name=client.name) }}" style="display:inline !important;">
    <input type="text" name="name" class="form-control" value="{{ client.name }}" >
    <input type="submit" style="position: absolute; left: -9999px; width: 1px; height: 1px;"/>
</form>

As you can see, the action attribute uses url_for to call the right method to edit the client. My method looks like this:

@mod.route('/edit/<name>/', methods=['POST'])
def edit_client_name(name):
    # code here    

The endspoints before and after an edit to a client name would be /edit/the_client_name and /edit/the_new_client_name as an example. Obviously, once the method has been called, my AJAX has to change this action attribute to replace it with the new URL (in case the user wants to re-edit the name without changing page/reloading the page). And here is my second issue.

The new name is stored in javascript while the action is still the old endpoint. Thus, I have to call url_for using the new name. And I have found no way to pass a Javascript variable to Jinja2.

I want this:

url_for('clients.edit_client_name', name=client.name)

To become this:

url_for('clients.edit_client_name', name=my_javascript_variable_one_way_or_another)

I'll just have to call the javascript function that modifies an attribute of a form, and modify my action attribute with this new url_for. But I have found no way to do this, i.e pass a javascript variable to Jinja2.


So here are my two issues. How can I make flash() compatible with AJAX? Also, how can I pass a javascript attribute to Jinja2 ?

The only solutions that I can see would be coding my own messaging system, which would defeat the goal of flash, making it useless with AJAX, which seems stupid, and hardcoding the URL in my templates, which totally defeats the initial interest of Flask making URL independent from methods, and URL changes extremely flexible.

Thanks.

I am currently stuck on a rather "big" problem, and I will try to be as clear and concise as I can about it. I am developing a tool in Python, using Flask. It's meant to be an intranet. The idea is that I have a client page. There's its name, numerous other infos, and a massive checklist.

The infos are already written in input fields, so that the user just has to edit them there, press enter, and the page is reloaded with the edited information (with a notification, using the flashed messages and growl).

The massive checklist is forcing me to move to a system using AJAX, since I want it to be updated "live" as users tick boxes and enter content in inputs, without reloading the page.

As a consequence, I'm also switching my basic inputs for infos to AJAX, to avoid the reloading of the page.

I've come across two issues :

  1. The first one concerns the message flashing feature of Flask.

    I've got a method that updates a client name in the database, flashes a message (success or failure, depending on numerous things), then displays the page again. To avoid the page reload, I'm managing the form submit with AJAX.

    The problem is, my python method will flash messages. And once I'm back on my html page (that has not been reloaded, since it's AJAX), the get_flashed_message function of Jinja2 returns me nothing, since it has not been updated. As a consequence, it's impossible for me to retrieve those flashed messages.

    How can I get these? The only solution I see is getting rid of any usage of flash, and coding my own messages system that I would return from the method, and then treat in javascript. This seems incredibly dumb, since flash is a feature of Flask, and it's made "useless" by AJAX. I feel like I'm missing something.

    (As a note, until now, my flashed messages management is made in my base layout, that calls a template that runs through all messages and displays them as growl notifications)

  2. Second issue concerns url_for. Here is my form, for editing the client's name :

<form id="changeClientName" method="POST" action="{{ url_for('clients.edit_client_name', name=client.name) }}" style="display:inline !important;">
    <input type="text" name="name" class="form-control" value="{{ client.name }}" >
    <input type="submit" style="position: absolute; left: -9999px; width: 1px; height: 1px;"/>
</form>

As you can see, the action attribute uses url_for to call the right method to edit the client. My method looks like this:

@mod.route('/edit/<name>/', methods=['POST'])
def edit_client_name(name):
    # code here    

The endspoints before and after an edit to a client name would be /edit/the_client_name and /edit/the_new_client_name as an example. Obviously, once the method has been called, my AJAX has to change this action attribute to replace it with the new URL (in case the user wants to re-edit the name without changing page/reloading the page). And here is my second issue.

The new name is stored in javascript while the action is still the old endpoint. Thus, I have to call url_for using the new name. And I have found no way to pass a Javascript variable to Jinja2.

I want this:

url_for('clients.edit_client_name', name=client.name)

To become this:

url_for('clients.edit_client_name', name=my_javascript_variable_one_way_or_another)

I'll just have to call the javascript function that modifies an attribute of a form, and modify my action attribute with this new url_for. But I have found no way to do this, i.e pass a javascript variable to Jinja2.


So here are my two issues. How can I make flash() compatible with AJAX? Also, how can I pass a javascript attribute to Jinja2 ?

The only solutions that I can see would be coding my own messaging system, which would defeat the goal of flash, making it useless with AJAX, which seems stupid, and hardcoding the URL in my templates, which totally defeats the initial interest of Flask making URL independent from methods, and URL changes extremely flexible.

Thanks.

Share Improve this question edited May 29, 2020 at 3:33 user5305519 3,2064 gold badges29 silver badges47 bronze badges asked Jun 18, 2014 at 14:35 lap0573lap0573 4632 gold badges6 silver badges12 bronze badges 10
  • 1 You could divide this in two separate questions you know? As for the first, you return the "flash message" as part of your JSON response and have the client side javascript handle showing it with the proper classes (i.e. error/success) – Oerd Commented Jun 18, 2014 at 14:47
  • 1 I think Oerd is right. Handle the flash message via JS. As to the second question, why don't you just pass something that will not change, like their id instead of their name? – Eric Workman Commented Jun 18, 2014 at 15:06
  • 1 @Eric Workman => Mmmh, well that's what I'll end up doing if I don't find another solution, but it fully negates any advantage given by flash(), which seems odd. I mean, this functinnality is basically worthless if you work with AJAX then ? As for your second solution, it may work indeed. The initial idea was since the user "saw" the URL, I wanted it to be clean and readable. But now that it's obfuscated behind AJAX, I could end up using the id. It's a very good idea, I think I'll do it this way then ! – lap0573 Commented Jun 18, 2014 at 15:07
  • 2 The usefulness of flash() feels very limited to me when working through AJAX. Perhaps you could put get_flashed_messages() in a route, call it via AJAX or as part of something else, and then handle the rendering with something simple in JS? – Eric Workman Commented Jun 18, 2014 at 15:16
  • 2 @user3360352 I mean you can just call it in your python code and return the results. get_flashed_messages() returns a list of the messages waiting to be displayed. So you can just use an ajax call to grab waiting messages instead of using get_flashed_messages() in the template. – Eric Workman Commented Jun 18, 2014 at 15:35
 |  Show 5 more comments

4 Answers 4

Reset to default 5

I'll take a stab at this, but I'm not sure I understand the problem completely :D. The code below isn't tested, it is more along the lines of pseudocode!

Your first problem is (if I understand you correctly) that you are doing partial updates via ajax, and wanting to fetch the update results later after the partial updates. Instead of fetching update results later, you should return them after each ajax call. So, if you have update code that looks like this:

data = $('#my-form').serialize()
$.post(update_url, data, function (results) {
    // Here you check results.
    if (results.success) {
        $('#update-div').message('Update Success')
    } else {
        $('#update-div').message('Update Failed: ' + results.error_msg)
    }
})

Then your update route would have code similar to this:

from flask import jsonify

@app.route('/partialupdate/<int:userid>', methods=['POST'])
def partial_update(userid):
    try:
        # fetch the user, perform the updates and commit
        return jsonify(success=1)
    except Exception, e:
        return jsonify(success=0, error_msg=str(e))

Your second problem involving URL generation can be fixed by using USERID instead of client name. NEVER use client name for anything other than searches :D. In this case use user id. The user id is typically the primary key for a particular user in a database. If you're not using a database, then generate your own user id's somehow and attach it to a user - it should be a unique number per user. Using a user id means that you can always edit with urls like '/edituser/1'.

I have had this requirement as well. To flesh out Paul Wand's answer, here is how I did it (note, the markup generated is for bootstrap 3:

put an empty div for the flash messages in the template:

<div id="flash"></div>

your ajax call:

$.post("/foo/bar", JSON.stringify({'calls': 'whatever'}), function(returnedData) {
    $('#flash').append(flashMessage(JSON.parse(returnedData)));
});

And use the parsing function:

var flashMessage = function(data){
  html = '';
  for (i=0; i<data.length; i++) {
    html += '<div class="alert alert-' + data[i]['type'] + '"><a href="#" class="close" data-dismiss="alert">&times;</a>' + data[i].message + '</div>';
  }
  return html;
};

in your route that handles the AJAX request, simply return a JSON object that looks like:

[{'type': 'success', 'message': 'Here is a message'}]

where the 'type' is a bootstrap 3 status type, ie. success, info, warning, danger. The dict gets wrapped in a list, so you can pass multiple if you need to.

The JSON.parse is not required if the response is already jsonified. In that case the code is simply:

$('#flash').append(flashMessage(returnedData));

Also, if you don't need multiple flash messages, simply replace 'append' with 'html'

You can write a simple javascript method to replace the functionality of flasks 'flash' method by understanding how flash_message.html is formatted.

function flash(message,category){
   if (category == 'error'){
      var icon='icon-exclamation-sign';
      category='danger';
      }
   else if (category == 'success')
      var icon='icon-ok-sign';
   else
      var icon='icon-info-sign';      
   $('<div class="alert alert-'+category+'"><i class="'+icon+'"></i>&nbsp;<a class="close" data-dismiss="alert">×</a>'+ message +'</div>').prependTo('#maincontent').hide().slideDown();
   $.smoothScroll({
     scrollElement: $('body'),
     scrollTarget: '#mainContent'
   });
}

url_for may be a little trickier,but possible.

Very old question, but I had the same problem and found a hacky workaround for getting flask's flashes through ajax. Basically what I did is I made a get_flashes route which returns the flashes template and I am using this route through jQuery's load function eg. $('#msg-div').load('{{ url_for('main.get_flashes') }}'

The get_flashes route:

@main.route('get-flashes')
def get_flashes():
    return render_template('_flashes.html')

_flashes.html (the template getting the flashes, see more in flask's docs)

{%- with messages = get_flashed_messages(with_categories=true) -%}
    {% if messages %}
        {% for category, message in messages %}
            {% if category == 'error' %}
            <div class="alert alert-danger alert-dismissable fade show msg" role="alert">
                {{ message }}
                <button type="button" class="close" data-dismiss="alert" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
            </div>
            {% else %}
            <div class="alert alert-{{ category }} alert-dismissable fade show msg" role="alert">
                {{ message }}
                <button type="button" class="close" data-dismiss="alert" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
            </div>
            {% endif %}
        {% endfor %}
    {% endif %}
{%- endwith %}
发布评论

评论列表(0)

  1. 暂无评论