Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use url_for if my method has multiple route annotations?

So I have a method that is accessible by multiple routes:

@app.route("/canonical/path/") @app.route("/alternate/path/") def foo():     return "hi!" 

Now, how can I call url_for("foo") and know that I will get the first route?

like image 523
jiggy Avatar asked Oct 16 '11 02:10

jiggy


People also ask

Can you use multiple decorators to route urls to a function in Flask?

The decorator syntax is just a little syntactic sugar. This code block shows an example using our custom decorator and the @login_required decorator from the Flask-Login extension. We can use multiple decorators by stacking them.

What does Flask route do?

App routing is used to map the specific URL with the associated function that is intended to perform some task. It is used to access some particular page like Flask Tutorial in the web application.

What is Url_for?

url_for is function in the Flask flask. helpers module. url_for generates a URL to an endpoint using the method passed in as an argument. Note that url_for is typically imported directly from flask instead of from flask. helpers , even though it is defined within the helpers module.


2 Answers

Ok. It took some delving into the werkzeug.routing and flask.helpers.url_for code, but I've figured out. You just change the endpoint for the route (in other words, you name your route)

@app.route("/canonical/path/", endpoint="foo-canonical") @app.route("/alternate/path/") def foo():     return "hi!"  @app.route("/wheee") def bar():     return "canonical path is %s, alternative is %s" % (url_for("foo-canonical"), url_for("foo")) 

will produce

canonical path is /canonical/path/, alternative is /alternate/path/

There is a drawback of this approach. Flask always binds the last defined route to the endpoint defined implicitly (foo in your code). Guess what happens if you redefine the endpoint? All your url_for('old_endpoint') will throw werkzeug.routing.BuildError. So, I guess the right solution for the whole issue is defining canonical path the last and name alternative:

"""     since url_for('foo') will be used for canonical path    we don't have other options rather then defining an endpoint for    alternative path, so we can use it with url_for """ @app.route('/alternative/path', endpoint='foo-alternative') """     we dont wanna mess with the endpoint here -     we want url_for('foo') to be pointing to the canonical path """ @app.route('/canonical/path')  def foo():     pass  @app.route('/wheee') def bar():     return "canonical path is %s, alternative is %s" % (url_for("foo"), url_for("foo-alternative")) 
like image 134
Nemoden Avatar answered Sep 23 '22 00:09

Nemoden


Rules in Flask are unique. If you define the absolute same URL to the same function it will by default clash because you're doing something which we stop you from doing since from our perspective that is wrong.

There is one reason why you would want to have more than one URL to the absolute same endpoint and that is backwards compatibility with a rule that existed in the past. Since WZ0.8 and Flask 0.8 you can explicitly specify an alias for a route:

@app.route('/') @app.route('/index.html', alias=True) def index():     return ... 

In this case if the user requests /index.html Flask will automatically issue a permanently redirect to just /.

That does not mean a function could not be bound to more than one url though, but in this case you would need to change the endpoint:

@app.route('/') def index():     ...  app.add_url_rule('/index.html', view_func=index, endpoint='alt_index') 

Or alternatively:

@app.route('/') @app.route('/index.html', endpoint='alt_index') def index():     ... 

In this case you can define a view a second time under a different name. However this is something you generally want to avoid because then the view function would have to check request.endpoint to see what is called. Instead better do something like this:

@app.route('/') def index():     return _index(alt=False)  @app.route('/index.html') def alt_index():     return _index(alt=True)  def _index(alt):     ... 

In both of these cases URL generation is url_for('index') or url_for('alt_index').

You can also do this on the routing system level:

@app.route('/', defaults={'alt': False}) @app.route('/index.html', defaults={'alt': True}) def index(alt):     ... 

In this case url generation is url_for('index', alt=True) or url_for('index', alt=False).

like image 28
Armin Ronacher Avatar answered Sep 24 '22 00:09

Armin Ronacher