一般我们通过一个URL就可以映射到某一个函数。反过来,知道一个函数时,也可以获得对应的URL,url_for()
函数可以进行反转、实现这个功能。
url_for()
函数接收一个及以上的参数,第一个参数是函数名,接收对应URL规则的命名参数,如果函数中有参数,则将这些参数传入url_for()
函数第一个参数的后面。
进行测试:
from flask import Flask,url_for
app = Flask(__name__)
@app.route('/')
def index():
# 根据函数的名字进行反转,得到函数对应的路由
print(url_for('article_list',aid=2))
return 'Hello World'
@app.route('/article/')
def article_list(aid):
return 'article %s' % aid
if __name__ == '__main__':
app.run(debug=True)
运行开启服务后访问http://127.0.0.1:5000/,打印
127.0.0.1 - - [09/Apr/2020 16:46:24] "GET /article/3 HTTP/1.1" 200 -
127.0.0.1 - - [09/Apr/2020 16:46:30] "GET / HTTP/1.1" 200 -
/article/2
显然,此时通过url_for()
方法得到了一个函数对应的路由,并显示在日志中;
有参数时,调用url_for()
方法也要传入参数。
通过url_for()
函数来构建URL从而在代码中拼URL的好处有两点:
url_for()
函数来获取地址而不用手动替换所有对应的URL。
url_for()
函数可以转义一些特殊字符和unicode字符串,这在有时候显得很有用。
进一步测试:
from flask import Flask,url_for
app = Flask(__name__)
@app.route('/')
def index():
print(url_for('article_list',aid=2))
print(url_for('notice'))
print(url_for('follow', fid=2,page=5))
return 'Hello World'
@app.route('/article/')
def article_list(aid):
return 'article %s' % aid
@app.route('/notice')
def notice():
return 'Notice is as follows'
@app.route('/follows/')
def follow(fid):
return 'Follower %s' % fid
if __name__ == '__main__':
app.run(debug=True)
再次访问,打印:
/article/2
/notice
/follows/2?page=5
显然,如果url_for()
方法中提供的参数多于传入的函数的参数时,以查询参数的形式返回路由。
斜杠/ 转码测试:
from flask import Flask,url_for
app = Flask(__name__)
@app.route('/')
def index():
print(url_for('follow', fid=2,page=5,param='/'))
return 'Hello World'
@app.route('/article/')
def article_list(aid):
return 'article %s' % aid
@app.route('/notice')
def notice():
return 'Notice is as follows'
@app.route('/follows/')
def follow(fid):
return 'Follower %s' % fid
if __name__ == '__main__':
app.run(debug=True)
打印:
127.0.0.1 - - [09/Apr/2020 17:07:09] "GET / HTTP/1.1" 200 -
/follows/2?page=5¶m=%2F
显然,此时 / 被编码成ASCII码 %2F。
2.指定URL末尾的斜杠有些URL的末尾是有斜杠的,有些URL末尾是没有斜杠的,这其实是两个不同的URL。
例如定义路由@app.route('/article/')
,当访问一个结尾不带斜线的URL/article
,会被重定向到带斜线的URL/article/
上去。
但是在定义路由的时候,如果在末尾没有加上斜杠,在访问的时候又加上了斜杠,这时候就会抛出一个404错误页面。例如@app.route("/article")
没有在末尾加斜杠,此时只能访问到/article
在访问/article/
的时候,就会抛出一个404错误。
因此建议在定义路由时加上末尾的/,这与在访问时加 / 与否都可以访问到。
默认情况下定义的路由只能使用GET请求,可以在@app.route()
中可以传入一个关键字参数methods来指定本方法支持的HTTP方法。
如果想用post请求,可以示意Postman工具来模拟,可点击https://download.csdn.net/download/CUFEECR/12319280进行下载,点击安装包进行安装,安装完成后直接打开运行,即可进行各种方法的网络请求。
先进行测试:
from flask import Flask,url_for,request
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello World'
@app.route('/article/')
def article_list(aid):
return 'article %s' % aid
@app.route('/login/')
def login():
return 'login'
if __name__ == '__main__':
app.run(debug=True)
服务运行之后,在Postman中进行测试,如下:
显然,此时Post方法不被允许,在路由中添加方法,再测试:
from flask import Flask,url_for,request
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello World'
@app.route('/article/')
def article_list(aid):
return 'article %s' % aid
@app.route('/login/', methods=['GET','POST'])
def login():
return 'login'
if __name__ == '__main__':
app.run(debug=True)
再在Postman中测试:
显然,装饰器将让login的URL既能支持GET方法又能支持POST方法,此时可以用POST方法请求到。
在给定了methods参数后,就只能用方法列表中的方法请求,如果列表中没有get方法则不能再用get方法进行请求。
还可以传入参数,在flask项目中接收参数。
测试如下:
from flask import Flask,url_for,request
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello World'
@app.route('/article/')
def article_list(aid):
return 'article %s' % aid
@app.route('/login/',methods=['GET','POST'])
def login():
print(request.form.get('name'))
return 'login'
if __name__ == '__main__':
app.run(debug=True)
Postman中请求:
查看控制台,会看到:
127.0.0.1 - - [09/Apr/2020 19:00:14] "POST /login/ HTTP/1.1" 200 -
corley
得到了请求的方法和得到的数据。
接收参数的方法:
get请求接收参数request.args.get('xxx')
post请求接收参数
request.form.get('xxx')
三、页面跳转和重定向
重定向在页面上体现的操作就是浏览器会从一个页面自动跳转到另外一个页面。
比如用户访问了一个需要权限的页面,但是该用户当前并没有登录,此时应该重定向到登录页面。
重定向分为:
在flask中,重定向是通过flask.redirect(url, code=302)
这个方法来实现的,url表示需要重定向到的URL,可以结合url_for()
函数来使用,code表示重定向类型状态码,默认是302也即暂时性重定向,可以修改成301来实现永久性重定向。
模拟访问个人中心页面,未携带name参数而重定向到登录页面:
from flask import Flask,url_for,request,redirect
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello World'
@app.route('/article/')
def article_list(aid):
return 'article %s' % aid
@app.route('/login/')
def login():
print(request.form.get('name'))
return 'login'
@app.route('/profile/')
def profile():
name = request.args.get('name')
if name:
return name
else:
return redirect('/login')
if __name__ == '__main__':
app.run(debug=True)
访问演示如下:
显然,请求中未携带name参数时会自动重定向到登录页面。
控制台中显示为:
127.0.0.1 - - [09/Apr/2020 19:23:09] "GET /profile/ HTTP/1.1" 302 -
127.0.0.1 - - [09/Apr/2020 19:23:09] "GET /login/ HTTP/1.1" 200 -
None
显然,请求状态码时302。
但是直接重定向到/login
显得不够灵活严谨,因为加入登录的地址改变,比如变成/signin
等,就必须重新修改代码,此时就可以用url_for()
方法,来传入方法名而不是路由地址,即便地址改变,也可以正常重定向,如下:
from flask import Flask,url_for,request,redirect
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello World'
@app.route('/article/')
def article_list(aid):
return 'article %s' % aid
@app.route('/login/')
def login():
print(request.form.get('name'))
return 'login'
@app.route('/profile/')
def profile():
name = request.args.get('name')
if name:
return name
else:
# return redirect('/login')
return redirect(url_for('login'))
if __name__ == '__main__':
app.run(debug=True)
效果与之前相同。
同时,也可以修改状态码为301,例如redirect(url_for('login'), code=301)
。
视图函数中可以返回以下类型的值:
Response对象。 字符串(response,status,headers)
,response为一个字符串,status值是状态码,headers是响应头。
如果不是以上三种类型,Flask会通过Response.force_type(rv,request.environ)
转换为一个请求对象。
之前在函数中返回的都是字符串,现进行返回列表的测试:
from flask import Flask,url_for,request,redirect
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello World'
@app.route('/article/')
def article_list(aid):
return 'article %s' % aid
@app.route('/login/')
def login():
print(request.form.get('name'))
return 'login'
@app.route('/profile/')
def profile():
name = request.args.get('name')
if name:
return name
else:
# return redirect('/login')
return redirect(url_for('login'))
@app.route('/about')
def about():
return ['123']
if __name__ == '__main__':
app.run(debug=True)
访问http://127.0.0.1:5000/about,会报错:
提示返回的类型只能是string、dict、tuple、Response instance或WSGI callable,其他类型会报错,现换成字典测试:
from flask import Flask,url_for,request,redirect
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello World'
@app.route('/article/')
def article_list(aid):
return 'article %s' % aid
@app.route('/login/')
def login():
print(request.form.get('name'))
return 'login'
@app.route('/profile/')
def profile():
name = request.args.get('name')
if name:
return name
else:
# return redirect('/login')
return redirect(url_for('login'))
@app.route('/about')
def about():
return {'name':'Corley'}
if __name__ == '__main__':
app.run(debug=True)
显示:
也可以返回元组,是元组时,只返回元组的第一个元素;
在一般情况下,返回元组的用法是return '关于我们',200
,即return '字符串',状态码
。
返回Response测试:
from flask import Flask,url_for,request,redirect,Response
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello World'
@app.route('/article/')
def article_list(aid):
return 'article %s' % aid
@app.route('/login/')
def login():
print(request.form.get('name'))
return 'login'
@app.route('/profile/')
def profile():
name = request.args.get('name')
if name:
return name
else:
# return redirect('/login')
return redirect(url_for('login'))
@app.route('/about')
def about():
return Response('关于我们')
if __name__ == '__main__':
app.run(debug=True)
显示:
可以看到,给Response传入字符串参数后,返回的和字符串是类似的;
Response('关于我们')
相当于Response('关于我们',status=200,mimetype='text/html')
。
Response的用法是Response('字符串',状态码,mimetype='')
也可以用make_response()
方法创建Response对象并返回,这个方法可以设置额外的数据,比如设置cookie、header等信息,测试如下:
from flask import Flask,url_for,request,redirect,Response,make_response
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello World'
@app.route('/article/')
def article_list(aid):
return 'article %s' % aid
@app.route('/login/')
def login():
print(request.form.get('name'))
return 'login'
@app.route('/profile/')
def profile():
name = request.args.get('name')
if name:
return name
else:
# return redirect('/login')
return redirect(url_for('login'))
@app.route('/about')
def about():
return make_response('关于我们')
if __name__ == '__main__':
app.run(debug=True)
效果与前者是一样的。
五、flask模板简介模板是一个web开发必备的模块,因为在渲染一个网页的时候,并不是只渲染一个纯文本字符串,而是需要渲染一个有富文本标签的页面,这时候就需要使用模板了。
在Flask中,配套的模板是Jinja2,Jinja2的作者也是Flask的作者。这个模板非常强大,并且执行效率高。
函数返回HTML代码测试:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return ''
if __name__ == '__main__':
app.run(debug=True)
显示:
显然,是可以解析HTML的,但是这很不利于前后端分离,从而给开发工作带来很大影响。
在一般开发时应该将HTML代码于Python代码分离,将前端代码放入templates目录中,再通过render_template()
方法来渲染模板即可。
在项目目录下创建templates目录,templates目录下创建index.html,再测试:
from flask import Flask,render_template
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run(debug=True)
显示与之前相同。
render_template()
方法根据传的值会在默认模板目录templates下寻找匹配模板。
在templates目录下再创建一个子目录,里边创建一个模板user.html:
用户中心
个人中心
此时要加载user.html必须添加完整的子文件夹路径,如下:
from flask import Flask,render_template
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
@app.route('/profile/')
def profile():
return render_template('profile/user.html')
if __name__ == '__main__':
app.run(debug=True)
显示:
如果想自定义模板保存目录,可以在初始化FLask时,传入template_folder参数来指定指定具体的路径,如下:
app = Flask(__name__, template_folder='./template_files')
此时你可以将所有模板文件放入template_files文件夹中,但是不建议自定义模板目录。
注意:
在模板代码中尽量不要含有注释,因为Flask也会解析注释掉的代码。
在Flask项目中给模板传入参数:
from flask import Flask,render_template
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html', username='小度', age=18,hobby='Basketball')
@app.route('/profile/')
def profile():
return render_template('profile/user.html')
if __name__ == '__main__':
app.run(debug=True)
模板index.html为:
Flask
{{username}}
{{age}}
{{hobby}}
运行起来之后可以看到:
显然,传递的参数值被渲染到了页面。
但是当模板中要传递的参数过多的时候,把所有参数放在一个函数中会显得传参部分的代码很臃肿,不是一个好的选择,此时可以进一步改进,使用字典进行包装,并且还可以加两个 * 号,来转换成关键字参数。
测试如下:
from flask import Flask,render_template
app = Flask(__name__)
@app.route('/')
def index():
context = {
'username':'小度',
'age':18,
'hobby':'Basketball'
}
return render_template('index.html', context=context)
@app.route('/profile/')
def profile():
return render_template('profile/user.html')
if __name__ == '__main__':
app.run(debug=True)
模板中也需要改变:
Flask
{{context.username}}
{{context.age}}
{{context.hobby}}
此时运行之后与之前效果相同。
当然,模板代码也可以不改变,即直接用username而不是context.username,
此时Python代码应该为:
from flask import Flask,render_template
app = Flask(__name__)
@app.route('/')
def index():
context = {
'username':'小度',
'age':18,
'hobby':'Basketball'
}
return render_template('index.html', **context)
@app.route('/profile/')
def profile():
return render_template('profile/user.html')
if __name__ == '__main__':
app.run(debug=True)
即用 ** 修饰context。
context中再嵌套字典测试:
from flask import Flask,render_template
app = Flask(__name__)
@app.route('/')
def index():
context = {
'username':'小度',
'age':18,
'hobby':{
'Basketball':'Basketball',
'Football':'Football',
'PingPong':'PingPong'
}
}
return render_template('index.html', **context)
@app.route('/profile/')
def profile():
return render_template('profile/user.html')
if __name__ == '__main__':
app.run(debug=True)
模板代码:
Flask
{{username}}
{{age}}
{{hobby.Basketball}}
{{hobby['Football']}}
{{hobby.PingPong}}
显示:
可以看到,嵌套字典取值的方法有2种:
hobby.Basketball
。
字典取值方法hobby['Football']
。
嵌套列表取值测试:
from flask import Flask,render_template
app = Flask(__name__)
@app.route('/')
def index():
context = {
'username':'小度',
'age':18,
'hobby':{
'Basketball':'Basketball',
'Football':'Football',
'PingPong':'PingPong'
},
'books':['Python','Java','PHP']
}
return render_template('index.html', **context)
@app.route('/profile/')
def profile():
return render_template('profile/user.html')
if __name__ == '__main__':
app.run(debug=True)
模板代码:
Flask
{{username}}
{{age}}
{{hobby.Basketball}}
{{hobby['Football']}}
{{hobby.PingPong}}
{{books.0}}
{{books[1]}}
{{books.2}}
显示:
显然,列表的取值也有两种方法:
books.0
。
列表取值方法books[1]
。
字典和列表的取值都有两种方法,建议使用属性取值方法,这在Flask中显得更地道。