烧瓶重定向多条路线
Posted
技术标签:
【中文标题】烧瓶重定向多条路线【英文标题】:Flask redirecting multiple routes 【发布时间】:2013-06-21 13:42:00 【问题描述】:我正在尝试实现重定向模式,类似于 *** 所做的:
@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)
下面是应该发生的事情的简单表格:
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
现在,我可以使用 /user/1/foo
访问配置文件,但 /user/1
会生成 BuildError
。我试过 alias=True
关键字参数和 defaults
的一些东西,但我不太确定什么不起作用。
我如何让一条路由像这样重定向到另一条路由?
【问题讨论】:
我不知道我是否遗漏了什么......但函数不应该在一个类内部并且有一个self
参数吗? (假设您使用的是flask classy)如果您使用蓝图可能会出错的其他事情是您应该将蓝图名称添加到url_for(flask.pocoo.org/docs/blueprints/#building-urls)。
如果您收到的是BuildError
,那么您对url_for
的调用就有问题。你能提供回溯吗?
@GabrielJordão:这是一个说明问题的简化示例。使用蓝图或 Flask-Classy 并不会真正改变任何东西。
你能发布你的app.url_map
吗?这应该有助于找出导致url_for
窒息的原因。
那么,呃……哪一个应该得到赏金?
【参考方案1】:
我不明白你为什么要重定向。您不会通过重定向获得任何收益,正如您自己提到的那样,您最终只是多次查询数据库。您不会以任何有意义的方式使用给定的用户名,因此请忽略它。
@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)
这将满足您给定的所有示例。
【讨论】:
Reddit 就是这样做的,但我更喜欢 *** 的方法。不管怎样,这只是我的例子。我正在寻找路由重定向问题的解决方案,而不是我的特定用例。【参考方案2】:调试路由:
更新:为了解决主要问题“我的路线出了什么问题”,最简单的调试方法是使用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>])
在这种情况下,这确认端点设置正确。
这是一个展示普通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)
它们有不同的端点,您需要考虑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>])
如果没有flask-classy
,端点的名称就是函数名称,但正如您所发现的,这与使用classy
时不同,您可以使用url_map()
查看端点名称或分配@route(..., endpoint='name')
在您的路线中。
更少的重定向:
要在最小化重定向数量的同时响应您发布的网址,您需要使用strict_slashes=False
,这将确保处理未以/
终止的请求,而不是使用301
重定向它们重定向到他们的/
-terminated 对应对象:
@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)
结果如下:
>>> 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)
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/"
我相信它完全符合您的测试矩阵的含义:)
【讨论】:
谢谢,这非常很有帮助。我也不知道strict_slashes
。【参考方案3】:
你几乎得到它。 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
是一个dict
,所有不在规则中的路由参数都有默认值。这里,在第二个路由装饰器中,规则中没有username
参数,所以必须在defaults
中设置。
【讨论】:
仅供参考,如果您的默认值对于同一函数的所有路由都相同,则可以将参数中的默认值设置为函数定义,而不是每个路由装饰器的默认字典。这样您只需在一个地方指定它们。 我们不能直接为函数提供默认值吗?例如,def profile(id, username=None):
@AdityaShaw 也许,我的回答已经超过 7 年了,而且我使用 Flask 已经有几年了。 API 可能在此期间发生了变化。【参考方案4】:
嗯,看起来我的原始代码确实有效。 Flask-Classy 是这里的问题(因为这个问题有赏金,我不能删除它)。
我忘记了 Flask-Classy 重命名路由,所以我必须选择最外层装饰器的路由,而不是 url_for('ClassName:profile')
:
url_for('ClassName:profile_1')
另一种方法是显式指定路由的端点:
@route('/<int:id>/<username>/', endpoint='ClassName:profile')
【讨论】:
是的,我怀疑是这样的,因此关于发布您的app.url_map
的问题。
@dnozay:谢谢,我从来不知道app.url_map
。它甚至可以漂亮地打印路线!以上是关于烧瓶重定向多条路线的主要内容,如果未能解决你的问题,请参考以下文章
从一个烧瓶应用程序重定向到本地主机上的另一个烧瓶应用程序[重复]