web-dev-qa-db-de.com

Wie verwende ich url_for, wenn meine Methode mehrere Routenanmerkungen enthält?

Ich habe also eine Methode, die auf mehreren Wegen zugänglich ist:

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

Wie kann ich nun url_for("foo") anrufen und wissen, dass ich die erste Route erhalte?

37
jiggy

OK. Es dauerte einige Eingriffe in den Code werkzeug.routing und flask.helpers.url_for, aber ich habe es herausgefunden. Sie ändern einfach die endpoint für die Route (dh Sie Name Ihre 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"))

wird herstellen

kanonischer Pfad ist/kanonisch/Pfad /, alternativ ist/alternativ/Pfad /

Es gibt einen Nachteil dieses Ansatzes. Die Flasche bindet immer die zuletzt definierte Route an den implizit definierten Endpunkt (foo in Ihrem Code). Ratet mal, was passiert, wenn Sie den Endpunkt neu definieren? Alle Ihre url_for('old_endpoint') werden werkzeug.routing.BuildError werfen. Ich denke, die richtige Lösung für das gesamte Problem besteht darin, den kanonischen Pfad als letzte und name alternative zu definieren:

""" 
   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"))
64
Nemoden

Regeln in Flask sind einzigartig. Wenn Sie dieselbe absolute URL für dieselbe Funktion definieren, stoßen Sie standardmäßig darauf, weil Sie etwas tun, von dem wir Sie abhalten, da dies aus unserer Sicht falsch ist.

Es gibt einen Grund, warum Sie mehr als eine URL zum absolut gleichen Endpunkt haben möchten, und das ist die Abwärtskompatibilität mit einer Regel, die in der Vergangenheit vorhanden war. Seit WZ0.8 und Flask 0.8 können Sie explizit einen Alias ​​für eine Route angeben:

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

Wenn der Benutzer /index.html anfordert, wird automatisch eine permanente Weiterleitung an / ausgegeben.

Das bedeutet nicht, dass eine Funktion nicht an mehr als eine URL gebunden sein könnte, aber in diesem Fall müssten Sie den Endpunkt ändern:

@app.route('/')
def index():
    ...

app.add_url_rule('/index.html', view_func=index, endpoint='alt_index')

Oder alternativ:

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

In diesem Fall können Sie eine Ansicht unter einem anderen Namen ein zweites Mal definieren. Dies ist jedoch etwas, das Sie generell vermeiden möchten, da dann die view-Funktion request.endpoint überprüfen muss, um zu sehen, was aufgerufen wird. Tun Sie lieber etwas wie folgt:

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

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

def _index(alt):
    ...

In beiden Fällen ist die URL-Generierung url_for('index') oder url_for('alt_index').

Sie können dies auch auf Routingsystemebene tun:

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

In diesem Fall ist die URL-Generierung url_for('index', alt=True) oder url_for('index', alt=False).

51
Armin Ronacher

Darüber hinaus gilt für diejenigen, die eine catch-all-Route verwenden, die mit Variablen erstellt wurde: Flask erstellt den URL-Pfad korrekt, wenn an url_for ein Wörterbuch übergeben wird, das die Variablen enthält.

Zum Beispiel...

app.py:

app.route('/<path:pattern1>')
app.route('/<path:pattern1>/<path:pattern2>')
def catch_all(pattern1, pattern2=None):
    return render_template('template.html', p1=pattern1, p2=pattern2)

app.route('/test')
def test_routing:
    args = {'pattern1': 'Posts', 'pattern2': 'create'}
    return render_template('test.html', args=args)

test.html:

<a href="{{url_for('catch_all', **args)}}">click here</a>

Wenn Sie auf den Link "Hier klicken" klicken, werden Sie zur Route "Posts/Create" geleitet.

0
vincent