Как мне использовать url_for, если в моем методе есть несколько аннотаций маршрутов? - PullRequest
37 голосов
/ 16 октября 2011

Итак, у меня есть метод, который доступен несколькими маршрутами:

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

Теперь, как мне позвонить url_for("foo") и узнать, что я получу первый маршрут?

Ответы [ 3 ]

65 голосов
/ 17 октября 2011

Хорошо.Потребовалось немного углубиться в код werkzeug.routing и flask.helpers.url_for, но я понял.Вы просто меняете endpoint для маршрута (другими словами, вы имя ваш маршрут)

@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"))

будет выдавать

канонический путь /канонический / путь /, альтернатива / альтернативный / путь /

Недостаток этого подхода.Flask всегда связывает последний определенный маршрут с конечной точкой, определенной неявно (foo в вашем коде).Угадайте, что произойдет, если вы переопределите конечную точку?Все ваши url_for('old_endpoint') будут выбрасывать werkzeug.routing.BuildError.Итак, я полагаю, что правильным решением для всей проблемы является определение канонического пути последнего и имя альтернатива:

""" 
   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"))
51 голосов
/ 24 октября 2011

Правила в колбе являются уникальными.Если вы определите абсолютно одинаковый URL для одной и той же функции, она по умолчанию будет конфликтовать, потому что вы делаете что-то, что мы запрещаем вам делать, поскольку с нашей точки зрения это неправильно.

Есть одна причина, по которой вы захотитеиметь более одного URL-адреса к одной и той же конечной точке, что обеспечивает обратную совместимость с правилом, существовавшим в прошлом.Начиная с WZ0.8 и Flask 0.8, вы можете явно указать псевдоним для маршрута:

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

В этом случае, если пользователь запрашивает /index.html Flask автоматически выдаст перманентное перенаправление на /.

Это не означает, что функция не может быть связана более чем с одним URL, но в этом случае вам нужно изменить конечную точку:

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

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

Или, альтернативно:

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

В этом случае вы можете определить представление во второй раз под другим именем.Однако это то, что вы обычно хотите избежать, потому что тогда функция представления должна будет проверить request.endpoint, чтобы увидеть, что вызывается.Вместо этого лучше сделайте что-то вроде этого:

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

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

def _index(alt):
    ...

В обоих этих случаях генерация URL-адреса url_for('index') или url_for('alt_index').

Вы также можете сделать это на уровне системы маршрутизации:

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

В этом случае генерация URL будет url_for('index', alt=True) или url_for('index', alt=False).

0 голосов
/ 25 марта 2015

Кроме того, для тех, кто использует перехват всех маршрутов, созданных с помощью переменных: Flask правильно создаст URL-путь, если url_for передан словарь, содержащий переменные.

Например ...

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>

При нажатии на ссылку «нажмите здесь» вы будете перенаправлены на маршрут «Сообщения / создать».

...