81

In Flask, when I have several routes for the same function, how can I know which route is used at the moment?

For example:

@app.route("/antitop/")
@app.route("/top/")
@requires_auth
def show_top():
    ....

How can I know, that now route was called using /top/ or /antitop/?

UPDATE

I know about request.path I don't want use it, because the request can be rather complex, and I want repeat the routing logic in the function. I think that the solution with url_rule it the best one.

Mohamed Diaby
  • 176
  • 2
  • 11
Igor Chubin
  • 61,765
  • 13
  • 122
  • 144

6 Answers6

117

Simply use request.path.

from flask import request

...

@app.route("/antitop/")
@app.route("/top/")
@requires_auth
def show_top():
    ... request.path ...
falsetru
  • 357,413
  • 63
  • 732
  • 636
72

the most 'flasky' way to check which route triggered your view is, by request.url_rule.

from flask import request

rule = request.url_rule

if 'antitop' in rule.rule:
    # request by '/antitop'

elif 'top' in rule.rule:
    # request by '/top'
Martin Thoma
  • 124,992
  • 159
  • 614
  • 958
thkang
  • 11,215
  • 14
  • 67
  • 83
  • 4
    This is a good solution but consider @marcinkuzminski 's solution . I think it is more elegant. – Ezequiel May 09 '19 at 14:33
  • This fails if the route is a [variable rule](https://flask.palletsprojects.com/en/2.0.x/quickstart/#variable-rules) `@app.route('/user/')` – gerardw Jun 26 '21 at 16:23
  • @gerardw `request.url_rule.rule` is the thing. It returns `/user/` as really needed. – winwin Sep 29 '21 at 13:54
59

Another option is to use endpoint variable:

@app.route("/api/v1/generate_data", methods=['POST'], endpoint='v1')
@app.route("/api/v2/generate_data", methods=['POST'], endpoint='v2')
def generate_data():
    version = request.endpoint
    return version
marcinkuzminski
  • 1,751
  • 14
  • 11
  • 1
    That shows the route with dots instead of slashes. That is, `subunit.page` instead of `/subunit/page` – gerardw Jun 26 '21 at 20:53
10

If you want different behaviour to each route, the right thing to do is create two function handlers.

@app.route("/antitop/")
@requires_auth
def top():
    ...

@app.route("/top/")
@requires_auth
def anti_top():
    ...

In some cases, your structure makes sense. You can set values per route.

@app.route("/antitop/", defaults={'_route': 'antitop'})
@app.route("/top/", defaults={'_route': 'top'})
@requires_auth
def show_top(_route):
    # use _route here
    ...
iurisilvio
  • 4,868
  • 1
  • 30
  • 36
2

It seems to me that if you have a situation where it matters, you shouldn't be using the same function in the first place. Split it out into two separate handlers, which each call a common fiction for the shared code.

Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
2

One thing I have to add to the answers above is that

request.path preserves the url parameter value(s) passed while
request.url_rule gives you the url_rule you defined without the passed parameter(s)

@app.route("/antitop/")
@app.route("/top/")
@requires_auth
def show_top():
    request.path
    request.url_rule
    # -> both will give you "/antitop/" or "/top/"
    ....

@app.route("/antitop/<username>")
@app.route("/top/<username>")
@requires_auth
def show_top():
    request.path
    # -> gives you "/antitop/name" or ...
    request.url_rule
    # -> gives you "/antitop/<username>" or ...
    ....

Since you don't have any variable routes definey (yet!) you don't have to care but for the sake of saving you work and the headache in the future I'd still suggest using request.path.

EDIT:
Also noting that
request.path returns <class 'str'> while
request.url_rule returns <class 'werkzeug.routing.rules.Rule'>
request.url_rule.rule returns a <class 'str'> again

Criomby
  • 76
  • 1
  • 7