Python Flask 重定向多条路由
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17285826/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Flask redirecting multiple routes
提问by Blender
I'm trying to implement a redirecting pattern, similar to what StackOverflow does:
我正在尝试实现一个重定向模式,类似于 StackOverflow 所做的:
@route('/<int:id>/<username>/')
@route('/<int:id>/')
def profile(id, username=None):
user = User.query.get_or_404(id)
if user.clean_username != username:
return redirect(url_for('profile', id=id, username=user.clean_username))
return render_template('user/profile.html', user=user)
Here's a simple table of what should happen:
这是应该发生的事情的简单表格:
URL Redirects/points to
====================================================
/user/123 /user/123/clean_username
/user/123/ /user/123/clean_username
/user/123/foo /user/123/clean_username
/user/123/clean_username /user/123/clean_username
/user/123/clean_username/ /user/123/clean_username/
/user/125698 404
Right now, I can access the profile with /user/1/foo
, but /user/1
produces a BuildError
. I've tried the alias=True
keyword argument and something with defaults
, but I'm not quite sure what isn't working.
现在,我可以使用 访问配置文件/user/1/foo
,但/user/1
会生成一个BuildError
. 我已经尝试过alias=True
关键字参数和带有 的东西defaults
,但我不太确定什么不起作用。
How would I have one route redirect to the other like this?
我如何让一条路线像这样重定向到另一条路线?
采纳答案by dnozay
debugging routes:
调试路线:
Update: to address the primary question "what's wrong with my routes", the simplest way to debug that is to use app.url_map
; e.g:
更新:要解决主要问题“我的路由出了什么问题”,最简单的调试方法是使用app.url_map
; 例如:
>>> app.url_map
Map([<Rule '/user/<id>/<username>/' (HEAD, OPTIONS, GET) -> profile>,
<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
<Rule '/user/<id>/' (HEAD, OPTIONS, GET) -> profile>])
In this case, this confirms that the endpoint is correctly set.
Here is an example showcasing both plain flask
and flask-classy
:
在这种情况下,这确认端点设置正确。这是一个示例,展示了普通flask
和flask-classy
:
from app import app, models
from flask import g, redirect, url_for, render_template, request
from flask.ext.classy import FlaskView, route
@app.route('/user/<int:id>', strict_slashes=False)
@app.route('/user/<int:id>/<username>', strict_slashes=False)
def profile(id, username=None):
user = models.User.query.get_or_404(id)
if user.clean_username != username:
return redirect(url_for('profile', id=id, username=user.clean_username))
return render_template('profile.html', user=user)
class ClassyUsersView(FlaskView):
@route('/<int:id>', strict_slashes=False)
@route('/<int:id>/<username>', strict_slashes=False, endpoint='classy_profile')
def profile(self, id, username=None):
user = models.User.query.get_or_404(id)
if user.clean_username != username:
return redirect(url_for('classy_profile', id=id, username=user.clean_username))
return render_template('profile.html', user=user)
ClassyUsersView.register(app)
They have different endpoints, which you need to take into account for url_for
:
它们有不同的端点,您需要考虑到url_for
:
>>> app.url_map
Map([<Rule '/classyusers/<id>/<username>' (HEAD, OPTIONS, GET) -> classy_profile>,
<Rule '/user/<id>/<username>' (HEAD, OPTIONS, GET) -> profile>,
<Rule '/classyusers/<id>' (HEAD, OPTIONS, GET) -> ClassyUsersView:profile_1>,
<Rule '/static/<filename>' (HEAD, OPTIONS, GET) -> static>,
<Rule '/user/<id>' (HEAD, OPTIONS, GET) -> profile>])
Without flask-classy
the name of the endpoint is the function name, but as you've found out, this is different for when using classy
, and you can either look at the endpoint name with url_map()
or assign it in your route with @route(..., endpoint='name')
.
没有flask-classy
端点的名称是函数名称,但正如您发现的那样,使用 时这是不同的classy
,您可以url_map()
使用@route(..., endpoint='name')
.
less redirects:
更少的重定向:
To respond to the urls you posted while minimizing the amount of redirects, you need to use strict_slashes=False
, this will make sure to handle requests that are not terminated with a /
instead of redirecting them with a 301
redirect to their /
-terminated counterpart:
要在最小化重定向数量的同时响应您发布的 url,您需要使用strict_slashes=False
,这将确保处理未以 a 终止的请求,/
而不是通过重定向将它们301
重定向到其/
终止的对应项:
@app.route('/user/<int:id>', strict_slashes=False)
@app.route('/user/<int:id>/<username>', strict_slashes=False)
def profile(id, username=None):
user = models.User.query.get_or_404(id)
if user.clean_username != username:
return redirect(url_for('profile', id=id, username=user.clean_username))
return render_template('profile.html', user=user)
here is the result:
结果如下:
>>> client = app.test_client()
>>> def check(url):
... r = client.get(url)
... return r.status, r.headers.get('location')
...
>>> check('/user/123')
('302 FOUND', 'http://localhost/user/123/johndoe')
>>> check('/user/123/')
('302 FOUND', 'http://localhost/user/123/johndoe')
>>> check('/user/123/foo')
('302 FOUND', 'http://localhost/user/123/johndoe')
>>> check('/user/123/johndoe')
('200 OK', None)
>>> check('/user/123/johndoe/')
('200 OK', None)
>>> check('/user/125698')
('404 NOT FOUND', None)
Behavior of strict_slashes
:
的行为strict_slashes
:
with strict_slashes=False
URL Redirects/points to # of redirects
===========================================================================
/user/123 302 /user/123/clean_username 1
/user/123/ 302 /user/123/clean_username 1
/user/123/foo 302 /user/123/clean_username 1
/user/123/foo/ 302 /user/123/clean_username 1
/user/123/clean_username 302 /user/123/clean_username 1
/user/123/clean_username/ 200 /user/123/clean_username/ 0
/user/125698 404
with strict_slashes=True (the default)
any non '/'-terminated urls redirect to their '/'-terminated counterpart
URL Redirects/points to # of redirects
===========================================================================
/user/123 301 /user/123/ 2
/user/123/foo 301 /user/123/foo/ 2
/user/123/clean_username 301 /user/123/clean_username/ 1
/user/123/ 302 /user/123/clean_username/ 1
/user/123/foo/ 302 /user/123/clean_username/ 1
/user/123/clean_username/ 200 /user/123/clean_username/ 0
/user/125698 404
example:
"/user/123/foo" not terminated with '/' -> redirects to "/user/123/foo/"
"/user/123/foo/" -> redirects to "/user/123/clean_username/"
I believe it does exactly what your test matrix is about :)
我相信它完全符合您的测试矩阵的含义:)
回答by Michael Davis
I don't understand why you are redirecting. You don't gain anything with the redirect and as you mentioned yourself, you end up just querying the database multiple times. You don't use the given username in any meaningful way, so just ignore it.
我不明白你为什么要重定向。重定向没有任何好处,正如您自己提到的,您最终只是多次查询数据库。您不会以任何有意义的方式使用给定的用户名,因此请忽略它。
@route('/<int:id>/<username>/')
@route('/<int:id>/')
def profile(id, username=None):
user = User.query.get_or_404(id)
return render_template('user/profile.html', user=user)
This will satisfy all of your given examples.
这将满足您所有给定的示例。
回答by dAnjou
You've almost got it. defaults
is what you want. Here is how it works:
你已经差不多了。defaults
是你想要的。下面是它的工作原理:
@route('/<int:id>/<username>/')
@route('/<int:id>/', defaults={'username': None})
def profile(id, username):
user = User.query.get_or_404(id)
if username is None or user.clean_username != username:
return redirect(url_for('profile', id=id, username=user.clean_username))
return render_template('user/profile.html', user=user)
defaults
is a dict
with default values for all route parameters that are not in the rule. Here, in the second route decorator there is no username
parameter in the rule, so you have to set it in defaults
.
defaults
是dict
所有不在规则中的路由参数的默认值。在这里,在第二个路由装饰器username
中,规则中没有参数,因此您必须将其设置在defaults
.
回答by Blender
Well, it looks like my original code actually worked. Flask-Classy was the issue here (and since this question has a bounty, I can't delete it).
好吧,看起来我的原始代码确实有效。Flask-Classy 是这里的问题(因为这个问题有悬赏,我不能删除它)。
I forgot that Flask-Classy renames routes, so instead of url_for('ClassName:profile')
, I'd have to select the outermost decorator's route:
我忘记了 Flask-Classy 重命名路由,所以url_for('ClassName:profile')
我必须选择最外层装饰器的路由,而不是:
url_for('ClassName:profile_1')
An alternative would be to explicitly specify an endpoint to the route:
另一种方法是明确指定路由的端点:
@route('/<int:id>/<username>/', endpoint='ClassName:profile')