Flask url_for URLs in Javascript

46,812

Solution 1

What @dumbmatter's suggesting is pretty much considered a de facto standard way. But I thought there would be a nicer way of doing it. So I managed to develop this plugin: Flask-JSGlue.

After adding {{ JSGlue.include() }}, you can do the following in your source code:

<script>
    $.post(Flask.url_for('comment.comment_reply', {article_id: 3}));
</script>

or:

<script>
    location.href = Flask.url_for('index', {});
</script>

Solution 2

The Flask documentation suggests using url_for in your HTML file to set a variable containing the root URL that you can access elsewhere. Then, you would have to manually build the view URLs on top of that, although I guess you could store them similar to the root URL.

Solution 3

Was searching for a solution, them come up with this solution where

  • No add-on is required

  • No hard-coding URL

The key is to realise Jinja2 has the ability to render javascript out of the box.


Setup your template folder to something like below:

/template
    /html
        /index.html
    /js
        /index.js

In your views.py

@app.route("/index_js")
def index_js():
    render_template("/js/index.js")

Now instead of serving the javascript from your static folder. You would use:

<script src="{{ url_for('index_js') }}"></script>

After all, you are generating the javascript on the fly, it is no longer a static file.


Now, inside of you javascript file, simply use the url_for as you would in any html file.

For example using Jquery to make an Ajax request

$.ajax({
  url: {{ url_for('endpoint_to_resource') }},
  method: "GET",
  success: call_back()
})

Solution 4

@jeremy's answer is correct and probably the more common approach, but as an alternative answer, note that dantezhu wrote and published a flask extension that claims to do the exact url-route-map-to-javascript translation suggested in the comment.

Solution 5

I use this dirty and ugly trick:

"{{url_for('mypage', name=metadata.name,scale=93,group=2)}}"
.replace('93/group',scale+'/group')

where scale is the javascript variable I want to use for an AJAX request. So, url_for generate an URL and JavaScript replace the parameter at runtime. The generated code is something like:

"/ajaxservive/mynam/scale/93/group/2".replace('93/group',scale+'/group')

which is pretty strange, but still can't find more elegant solutions.

In fact, looking at the code of the linked flask extension, it does the same thing but managing the general case.

Share:
46,812

Related videos on Youtube

Erik Rothoff
Author by

Erik Rothoff

I love code.

Updated on July 09, 2022

Comments

  • Erik Rothoff
    Erik Rothoff almost 2 years

    What is the recommended way to create dynamic URLs in Javascript files when using flask? In the jinja2 templates and within the python views url_for is used, what is the recommended way to do this in .js files? Since they are not interpreted by the template engine.

    What basically want to do is:

    // in comments.js
    $.post(url_for('comment.comment_reply'));
    

    Which is not possible.

    But naturally, I can execute that in a template:

    <script>
        $.post(url_for('comment.comment_reply'));
    </script>
    
  • Erik Rothoff
    Erik Rothoff about 12 years
    Not that pretty, but I guess it will have to do!
  • dumbmatter
    dumbmatter about 12 years
    Now that I think about it again, wouldn't it be possible to write a Flask extension that sees the URL routing defined in the Python code and dynamically translates it (whenever something changes) into equivalent JavaScript code, thus providing an equivalent url_for in JavaScript?
  • Erik Rothoff
    Erik Rothoff over 9 years
    Awesome! Exactly what I was looking for in 2012. :) I'll change the accepted answer as this is a lot nicer
  • ThatAintWorking
    ThatAintWorking about 9 years
    Does this allow you to use javascript variables at page-runtime to specify parameters to url_for of building the url? Is that what the article_id in your example code signifies?
  • Stewart Park
    Stewart Park about 9 years
    Yes, @ThatAintWorking. This can be used as exactly the same as what you would use in the view functions on Flask. And it is a client-side implementation.
  • ThatAintWorking
    ThatAintWorking about 9 years
    I'm sorry, I should have come back and removed or updated my comment. I posted it before really checking out your library. I'm already using it in my project. Thanks for making it.
  • tmthyjames
    tmthyjames almost 9 years
    I love the idea of this! I couldn't get it working; I passed the jsglue variable in the render_template method and then added this {{ JSGlue.include() }}, but when I tried to use Flask.url_for() in my javascript, I got Uncaught ReferenceError: Flask is not defined. It is completely possible that I missed something though. Anyways, good stuff!
  • Stewart Park
    Stewart Park almost 9 years
    @tmthyjames You don't have to pass it in the render_template method; you just create the JSGlue instance with the Flask app instance and use it directly from templates. JSGlue.include() just adds a <script> tag to include the Flask javascript object in the front-end side. Hope you'd get it working!
  • SUNDONG
    SUNDONG over 8 years
    This is very nice solution. I couldn't change script file path, which in my case was d3.json("{{url_for('static', filename='userdata/0af968589866013dbe09f57d3c78cc094666ff49.‌​json')}}", because file path information is inside {{ url_for }} command, so I couldn't break. However, now I can define var usermusics as 0afblahblah.json and use it like Flask.url_for("static", {"filename": usermusics})
  • Talia
    Talia about 8 years
    This looks really convenient, but has anybody considered the security implications of exposing this part of the Flask API to the public?
  • Talia
    Talia about 8 years
    This is actually potentially even worse: It actually lists all your endpoints and information about their parameters in the JS file.
  • Talia
    Talia about 8 years
    I've actually used this solution, but it's important to note the performance impact of rendering all your javascript files with jinja2, and its potential impact on caching.
  • Talia
    Talia about 8 years
    This could be improved significantly if it only included endpoints whitelisted by a decorator.
  • Stewart Park
    Stewart Park almost 8 years
    Decorator sounds like a good idea. But to be fair, having an endpoint not exposed doesn't make it secure. You have to have some kind of authentication/security other than just not exposing endpoints. I'll add this to the roadmap, I think that's a valid/more secure approach. Thanks for the input, @Collin.
  • Talia
    Talia almost 8 years
    Awesome! Right, I was talking only about endpoints that someone doesn't have access to. That is, ones where they would get a 403/404/redirect-to-login-page if they were to try to access. No reason to list those endpoints publicly, and listing them publicly could make it easier for an adversary to find a security vulnerability if there is one.
  • jpmc26
    jpmc26 over 7 years
    @Collin In security, you generally assume your attacker knows everything about how your system works that you do (note that "how your system works" does exclude secret passwords, keys, etc.). Your system should be secure even if the attacker knows the end points. There are other ways of getting them, e.g. social engineering, infecting a legitimate user's machine, and I'm sure the list goes on. Relying on an attacker not knowing the endpoint is a form of "security through obscurity," and in practice, such techniques generally don't hold up well to a real attacker.
  • jpmc26
    jpmc26 over 7 years
    @Collin Also keep in mind that your URLs are fundamentally public information. Many of your users will know them, so they would be difficult to protect anyway. And you're really only exposing a pattern, so an attacker still needs the inputs to the pattern. I don't think this small exposure of information greatly increases your risk.
  • Talia
    Talia over 7 years
    @jpmc26 You're absolutely right that that's how it should work. Unfortunately, inherited codebases are a thing....
  • jpmc26
    jpmc26 over 7 years
    @Collin That's the point you go to your manager/client and say, "This application as it exists now has fundamental security flaws that should be addressed immediately if you value your reputation. Here is what I propose..."
  • Employee
    Employee over 5 years
    I have a question: How would it be possible to integrate Flask-JSGlue not on a standard Flask application but on a socketized Flask-SocketIO application? I'm trying to do so with no luck, I've opened a question for it at this link - stackoverflow.com/questions/54310058/… - if anybody knows how to do this, any help would be highly appreciated
  • bluenote10
    bluenote10 about 5 years
    Adding a bit more details to the answer would be helpful. I'm doing exactly what the link recommends, but the template variable request.script_root|tojson|safe renders to an empty string for me. Unfortunately the doc link doesn't give a self-contained example and I can't figure out what it is assuming implicitly...
  • Mojimi
    Mojimi almost 5 years
    Yup, adding to this, if you are needing a url_for that is specifically for Javascript and doesn't fit into the HTML template, then you should probably be writing a small JS lib to do your AJAX requests. This lib could read the urls from a JSON that is dynamically created by Jinja2
  • marvin
    marvin over 4 years
    Thanks for the alternate solution. Note that you have a typo in your view function. It should be return render_template("/js/index.js") rather than render_template("/js/index.js")