Flask 依赖两个外部库:WerkZeug 和 Jinja2。WerkZeug 是一个WSGI(在web应用和多种服务器之间的标准Python 接口)工具集。Jinja2负责渲染模板。所以在安装Flask之前,需要安装这两个外部库,而最简单的方法就是使用 Virtualenv 创建虚拟环境
pip install virtualenv
检验
virtualenv --version
下一步就是使用Virtualenv 命令在当前文件夹创建Python虚拟环境。创建虚拟环境之后当前文件夹会出现一个子文件夹,名字就是下述命令中指定的参数
virtualenv venv
venv\Scripts\activate
pip install flask
安装校验
pip list --format columns
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run(debug=True)
(1)首先我们导入了Flask类。这个类的实例将会是我们的WSGI应用程序
(2)接下来,我们创建了一个该类的实例,第一个参数是应用模块或者包名的名称。如果你还用单一的模块,比如本例,你应该使用__name__
,因为模块的名称将会因其作为一个单独的应用模块启动还是作为模块导入而有不同(也是__main_
或者实际的导入名)。这是必须得,这样Flask才知道到哪儿去模板,静态文件等。详情见Flask的文档。
(3)然后,我们使用route()装饰器告诉Flask什么样的URL能触发我们的函数
(4)这个函数的名字也在生成URL时被特定的函数采用,这个函数返回我们想要显示在用户浏览器中的信息
(5)最后我们使用run()函数来让应用运行在本地的服务器上面。其中if__name __=='__main__':
确保服务器只会在该脚本被Python解释器直接执行的时候才会运行,而不是作为模板导入的时候
虽然run()方法使用与启动本地的开发服务,但是用户没每次修改代码后需要手动的重启服务,这样并不优雅,而Flask可以做到更好。如果你启用了调试支持,服务器会在代码修改后自动重新载入,并且在发生错误的时候提供一个相当可用的调试器 相当于Java的热部署
使用方法,直接在代码中添加
app.run(debug=True)
或者
app.debug=True
app.run()
处理URL和函数之间的关系的程序称之为路由
@app.route('/')
def hello_world():
return 'Hello World!
'
修饰器是Python语言的标准特性,可以使用不同的方法修改函数的行为。惯常用法是使用修饰器把函数注册为事件的处理程序
要给URL添加变量部分,你可以把这些特殊的字段编辑为
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
@app.route('/user/')
def show_user_profile(username):
# 显示该用户名的用户信息
return 'User %s' % username
@app.route('/post/')
def show_post(post_id):
# 根据ID显示文章,ID是整型数据
return 'Post %d' % post_id
if __name__ == '__main__':
app.run(debug=True)
Flask能够匹配URL,而且还可以用url_for()来给指定的函数构造URL。它接收函数名作为第一个参数,也接受对应URL规则的变量部分的命名参数。未知变量部分会添加到URL末尾作为查询条件
from flask import Flask , url_for
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
@app.route('/user/' )
def show_user_profile(username):
# 显示该用户名的用户信息
return 'User %s' % username
@app.route('/post/' )
def show_post(post_id):
# 根据ID显示文章,ID是整型数据
return 'Post %d' % post_id
@app.route('/url/')
def get_url():
# 根据ID显示文章,ID是整型数据
return url_for('show_post',post_id=2)
if __name__ == '__main__':
app.run(debug=True)
HTTP(与web应用会话的协议)有许多的不同的构造方法访问URL方法。默认情况下,路由只回应GET请求,当时通过route()装饰器传递methods 参数可以改变这个行为。
@app.route('/', methods=['GET', 'POST'])
def index():
if request.methods=='POST':
pass
else:
pass
url_for('static',filename='style.css'),
# 这个文件应该存储在文件系统上的static/style.css
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def hello_world():
return render_template('index.html')
@app.route('/user/' )
def show_user_profile(username):
# 显示该用户名的用户信息
return render_template('user.html', name=username)
if __name__ == '__main__':
app.run(debug=True)
模板是一个包含响应文本的额文件,其中包含用占位变量表示的动态部分,其具体值只在请求的上下文中才能知道。使用真实值替代变量,再返回最终得到的响应字符串,这一过程称为渲染。为了渲染模板,Flask使用了一个名为Jinja2的强大模板引擎
默认情况下,Flask在程序文件夹中的templates 子文件夹中寻找模板。
比如:
我们可以在模板中使用变量{{name}},这表示一个占位符,而name是变量名称;
模板学习
<p>从一个字典中获取一个值:{{mydict['key']}}.p>
<p>从一个列表中获取一个值:{{mylist[3]}}.p>
<p>从一个列表中获取一个带索引的值:{{mylist[myintvar]}}.p>
<p>从一个对象的方法中获取一个值:{{myobj.somemethod()}}.p>
可以使用过滤器修改变量,过滤器添加变量名之后,中间使用竖线分隔。
Hello,{{name|capitalize}}
if…else…endif
{% if user %}
Hello,{{user}}
{% else %}
Hello Stranger
{% endif %}
{% for comment in comments %}
{{comment}}
{% endfor %}
宏类似于Python代码中的函数
{% macro render_comment(comment) %}
{{comment}}
{% endmacro %}
{% for comment in comments %}
{{render_comment(comment)}}
{% endfor %}
需要在多处重复使用的模板代码片段可以写在独立的文件,在包含在所有的模板中,以免重复
方法一:全部包含
{% include 'common.html' %}
方法二:继承包含
base.html
{%block head%}
{%block title%}{%endblock%}-My Application
<%endblock%>
{%bolck body%}
{%endbody%}
bolck标签定义的元素可在衍生模板中修改。在本例中,我们定义了名为head、title、body的块。注意。title包含在head中。
举例衍生模板
{% extends "base.html"}
{%block title%}Index{%endhead%}
{%block head%}
{{supper()}}
<style>
style>
{% endblock %}
{%bolck body%}
<h1>Helloh1>
{%endbody%}
extends 指令声明这个模板衍生于base,html,在extends指令之后,基模板的三个块被重新定义,模板引擎将会在其插入适当的位置,注意新定义的head块,在基模板中其内容不是空的,所以用supper()获取原来的内容
pip install flask-wtf
官网
CSRF跨站请求伪造,源于WEB的隐式身份验证机制。WEB的身份验证机制虽然可以抱着一个请求时来自于某个用户的浏览器,但却无法保证该请求是用户批准发送的。
例如,用户登录受信任的网址A,在本地生成了Cookie,在Cookie没有失效的情况下去访问了危险网站B。B可能会盗用你的身份,以你的名义去发送恶意请求,邮件,盗取你的账号,设置购买商品,造成你个人隐私泄露,已经财产安全。
实际上,WTForms在渲染没一个表单都会生成一个独一无二的token,这个token将在POST请求的时候随着表单一起提交进行验证。默认情况下Flask-WTF能保护所有表单免受跨域请求伪造的攻击,恶意网站把请求发送到被攻击者已经登陆的网站就会引发CSRF攻击。为了实现CSRF保护,Flask-WTF需要程序设置加密令牌,再用加密令牌来验证表单数据的真伪。
使用方法
app = Flask(__name__)
app.config['SECRET_KEY'] = 'abcd123'
使用Flask-WTF时,每个表单都由一个继承自Form的类表示,这个类定义表单中的一组字段。
每个字段有用对象表示,并且字段可以附属一个或者多个校验用来验证用户输入的数据是否符合要求。
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import Required
class NameForm():
name = StringField('请输入姓名', validators=[Required()])
password = PasswordField('请输入密码', validators=[Required()])
submit = SubmitField('Submit')
这个表单中所有字段都定义为类变量,类变量的值是相应字段类型的对象
在上述代码中我们使用了三个HTML标准字段,更多参考如下:
表单字段是可调用的,在模板中渲染会生成HTML。
假设视图函数把一个NameForm实力通过参数传进form表单,在模板中可以生成一个简单的表单验证用户登录信息。
创建一个model.py文件定义一个表单类LoginForm,其中有三个属性分别是name,password和submit,具体代码如下:
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField,SubmitField
from wtforms.validators import DataRequired,Length
class LoginForm(FlaskForm):
"""
登录表单类
"""
name = StringField(label='用户名', validators=[
DataRequired("用户名不能为空"),
Length(max=10,min=3,message="用户名长度必须大于3且小于8")
])
password = PasswordField(label='密码', validators=[
DataRequired("密码不能为空"),
Length(max=10, min=6, message="用户名长度必须大于6且小于10")
])
submit = SubmitField(label="提交")
在文件中分别创建首页视图index()函数和登录页视图login()函数。引入我们刚才创建的model.py文件并在login()视图函数中对用户登陆进行验证,验证通过则跳转至首页,否则提示错误
from flask import Flask ,url_for,redirect, render_template
from models import LoginForm
app = Flask(__name__)
app.config['SECRET_KEY'] = 'mrsoft'
@app.route('/login', methods=['GET', 'POST'])
def login():
"""
登录页面
"""
form = LoginForm()
if form.validate_on_submit():
username = form.name.data
password = form.password.data
if username== "admin" and password == "abcd1234":
return redirect(url_for('index'))
return render_template('login.html',form=form)
@app.route('/')
def index():
"""
首页
"""
name = "班德尔城"
message = """
约德尔人的故乡究竟在何处?对于这个问题人们众说纷纭,不过有一些凡人声称自己穿过了无形的传送门,进入了超越物质领域的奇异魔法世界。他们都描述了一个魔法奔放的地方,鲁莽蛮横的人会被无数的奇观带入歧途,最后迷失在梦境中,永远无法返回……
在班德尔城,任何约德尔人以外的种族都会感到自己的全部感官得到了强化。城中所见无不色彩斑斓,食物与水的味道让人经年沉醉——只要尝过一次,就终身难忘。这里日光溶溶,春水不休,每一株植物都会结出累累硕果。或许这些描述中有一部分属实,或许全都是假的——因为没有任何两个讲述者能够对所见所闻达成一致。
只有一件事可以肯定,那就是班德尔城和里面的居民都具有某种超脱时间的属性,这或许可以解释为什么从那里回来的凡人全都像是一夜苍老了许多年岁,更多人则根本是一去不复返。
"""
return render_template("index.html",name=name,message=message)
if __name__ == '__main__':
app.run(debug=True,
port=8000)
渲染login.html和index.html如下:
<div class="jumbotron">
<form action="" method="post">
<div class="form-group">
{{ form.name.label }}
{{ form.name(class="form-control")}}
{% for err in form.name.errors %}
<p style="color: red">{{ err }}</p>
{% endfor %}
</div>
<div class="form-group">
{{ form.password.label }}
{{ form.password(class="form-control") }}
{% for err in form.password.errors %}
<p style="color: red">{{ err }}</p>
{% endfor %}
</div>
{{ form.csrf_token }}
{{ form.submit(class="btn btn-primary") }}
</form>
</div>
<div class="jumbotron">
<h1 class="display-3">欢迎来到{{name}}</h1>
<p >
{{message}}
</p>
<hr class="my-4">
<p>
<a class="btn btn-primary btn-lg" href="#" role="button">了解更多</a>
</p>
</div>
当我们运行run.py函数,浏览器输入
http://127.0.0.1:5000/login