参考文献:http://docs.jinkan.org/docs/flask/quickstart.html
https://www.jianshu.com/p/24c7c416bbb6
客户端session导致的问题:https://xz.aliyun.com/t/2218
Flask是一个Python编写的Web 微框架,让我们可以使用Python语言快速实现一个网站或Web服务。Flask依赖两个外部库:Werkzeug和Jinja2。前者是一个WSGI套件。WSGI是Web应用与多种服务器之间的标准Python接口,用于开发,也用于部署。
安装Flask
直接使用pip。
pip install flask
学新的东西,最先输出个hello world
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
将这个py文件运行一下,然后
在地址栏中输入
http://127.0.0.1:5000/
出现了
Hello World!
- 首先,导入了Flask类。这个类的实例将会是 WSGI 应用程序。
- 接下来,创建一个该类的实例,第一个参数是应用模块或者包的名称。 如果使用单一的模块(如本例),应该使用
__name__
,因为模块的名称将会因其作为单独应用启动还是作为模块导入而有不同( 也即是__main__
或实际的导入名)。这是必须的,这样 Flask 才知道到哪去找模板、静态文件等等。 - 然后,我们使用
route()
装饰器告诉 Flask 什么样的URL 能触发我们的函数。 - 这个函数的名字也在生成 URL 时被特定的函数采用,这个函数返回我们想要显示在用户浏览器中的信息。
- 最后我们用
run()
函数来让应用运行在本地服务器上。 其中if __name__ == '__main__':
确保服务器只会在该脚本被 Python 解释器直接执行的时候才会运行,而不是作为模块导入的时候。
包含动态路由的程序
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
@app.route('/user/<name>')
def user(name):
return "<h1>Hello,%s!</h1>" % name
if __name__ == '__main__':
app.run()
运行这个程序前,需要把hello.py重新运行一下,然后输入http://127.0.0.1:5000/user/SHm
会出现Hello,SHm!
模板
Flask在程序文件夹中的templates
子文件夹中寻找模板,在static
子文件夹中寻找静态文件。
①.render_template
函数:将Jinja2模板引擎集成到程序中。需导入from flask import Flask,render_template
,使用方式render_template('index.html',form=form,name=name)
,第一个参数为模板的文件名,随后是模板变量对应的真实值。
②. {{name}}
结构表示一个变量,这个位置的值从渲染模板时使用的数据中获取。
③.Bootstrap是客户端框架。服务器需要做的只是提供引用了Bootstrap的css和JS文件的HTML响应。需要导入from flask_bootstrap import Bootstrap
模块,引用bootstrap = Bootstrap(app)
④.bootstrap/wtf.html
文件中定义了一个使用Bootstrap渲染Flask-WTF表单对象的辅助函数。{% import 'bootstrap/wtf.html' as wtf %}
,{{ wtf.quick_form(form) }}
为Flask-WTF表单对象,使用Bootstrap的默认样式渲染传入的表单,渲染NameForm对象
web表单
使用Flask-WTF时,每个web表单都由一个继承自Form的类表示。
#hello.py
from flask import Flask, render_template
from flask_wtf import FlaskForm
from wtforms import StringField,SubmitField
from wtforms.validators import Required
from flask_bootstrap import Bootstrap
#表单类,有一个name的文本字段和一个submit的提交按钮
class NameForm(FlaskForm):
name= StringField('What is your name?', validators=[Required()])
submit = SubmitField('Submit')
app = Flask(__name__)
bootstrap = Bootstrap(app)
#设置密钥生成加密令牌,严重表单数据的真伪
app.config['SECRET_KEY']='you will never guess'
#路由方法
#methods参数在URL映射中将这个视图函数注册为GET和POST请求的处理程序。若无参数,则为GET请求
@app.route('/',methods=['GEt','POST'])
def index():
name=None
form=NameForm()
if form.validate_on_submit():
session['name']=form.name.data
#重定向
return redirect(url_for('index'))
return render_template('index.html',form=form,name=name)
if __name__ == '__main__':
app.run()
#templates/base.html
{% extends 'bootstrap/base.html' %}
{% block title %}Flasky{% endblock %}
{% block navbar %}
<div class="navbar navbar-inverse" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">Flasky</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a href="/">Home</a></li>
</ul>
</div>
</div>
</div>
{% endblock %}
{% block content %}
<div class="container">
{% block page_content %}{% endblock %}
</div>
{% endblock %}
#templates/index.html
{% extends "base.html" %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block title %}Flasky{% endblock %}
{% block page_content %}
<div class="page-header">
#条件语句,若结果为ture,则渲染`if和else`指令之间的值,反之,则渲染`else和endif`指令之间的值
<h1>Hello,{% if name %}{{name}}{% else %}Stranger{% endif %}!</h1>
</div>
{{ wtf.quick_form(form) }}
{% endblock %}
Flask
连接数据库:
安装需要的模块
pip install mysql-python//若是报错,则运行 apt-get install libmysqlclient-dev
pip install SQLAlchemy//数据库的ORM框架
pip install Flask-SQLAlchemy
通过SQLAlchemy连接数据库
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import os
basedir = os.path.abspath(os.path.dirname(__file__))
app=Flask(__name__)
#程序使用的数据库URL必须保存到SQLALCHEMY_DATABASE_URI变量
app.config['SQLALCHEMY_DATABASE_URI']='sqlite:///'+os.path.join(basedir,'data.sqlite')
# 每次请求结束都会自动提交事务
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN']=True
#Flask-SQLAlchemy 将会追踪对象的修改并且发送信号
app.config['SQLALCHEMY_TRACK_MODIFICATIONS']=False
db=SQLAlchemy(app)
#Flask-SQLAlchemy要求每个模型都要定义主键,这一列经常命名为id
class Role(db.Model):
#数据库中使用的表名
__tablename__='roles'
id=db.Column(db.Integer,primary_key=True)
name=db.Column(db.String(64),unique=True)
#面向对象视角。对于一个Role类的实例,其users属性将返回与角色相关联的用户组成的列表。
db.relationship()的第一个参数表明这个关系的另一端是那个模型,第二个参数向User模型中添加一个Role模型,定义反向关系,此时获取的是模型对象
users=db.relationship('User',backref='role',lazy='dynamic')
def __repr__(self):
return '<Role %r>'% self.name
class User(db.Model):
__tablename__='users'
id=db.Column(db.Integer,primary_key=True)
username=db.Column(db.String(64),unique=True,index=True)
#外键,就是这个外键建立了关系。传给db.ForeignKey()的参数表明,这列的值是roles表中的行的id值。
role_id=db.Column(db.Integer,db.ForeignKey('roles.id'))
def __repr__(self):
return '<User %r>'% self.username
使用方式:
#python sql.py shell
# python
创建表
>>> from sql import db
>>> db.create_all()
插入行
>>> from sql import Role,User
>>> admin_role = Role(name='Admin')
>>> mod_role = Role(name='Moderator')
>>> user_role = Role(name='User')
>>> user_john = User(username='john', role=admin_role)
>>> user_susan = User(username='susan', role=user_role)
>>> user_david = User(username='david', role=user_role)
批量添加到会话中
>>> db.session.add_all([admin_role,mod_role,user_john,user_susan,user_david])
提交给数据库
>>> db.session.commit()
修改数据
>>>admin_role.name = 'test'
>>>db.session.add(admin_role)
>>>db.session.commit()
删除行
>>>db.session.delete(mod_role)
>>>db.session.commit()
查询数据
>>>Role.query.all()
>>>User.query.filter_by(role=user_role).all()
>>>user_role = Role.query.filter_by(name='User').first()
在视图函数中操作数据库
from flask import Flask, render_template,session,redirect,url_for,flash
from flask_wtf import FlaskForm
from wtforms import StringField,SubmitField
from wtforms.validators import Required
from flask_bootstrap import Bootstrap
from flask_sqlalchemy import SQLAlchemy
import os
basedir = os.path.abspath(os.path.dirname(__file__))
app = Flask(__name__)
bootstrap = Bootstrap(app)
app.config['SECRET_KEY']='you will never guess'
#连接mysql数据库的账号密码以及创建好的数据库
app.config['SQLALCHEMY_DATABASE_URI']=mysql://root:password@localhost:3306/flaskr
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN']=True
app.config['SQLALCHEMY_TRACK_MODIFICATIONS']=False
db=SQLAlchemy(app)
class NameForm(FlaskForm):
name= StringField('What is your name?', validators=[Required()])
submit = SubmitField('Submit')
class Role(db.Model):
__tablename__='roles'
id=db.Column(db.Integer,primary_key=True)
name=db.Column(db.String(64),unique=True)
users=db.relationship('User',backref='role',lazy='dynamic')
def __repr__(self):
return '<Role %r>'% self.name
class User(db.Model):
__tablename__='users'
id=db.Column(db.Integer,primary_key=True)
username=db.Column(db.String(64),unique=True,index=True)
role_id=db.Column(db.Integer,db.ForeignKey('roles.id'))
def __repr__(self):
return '<User %r>'% self.username
@app.route('/',methods=['GEt','POST'])
def index():
name=None
form=NameForm()
if form.validate_on_submit():
user = User.query.filter_by(username=form.name.data).first()
if user is None:
user = User(username=form.name.data)
db.session.add(user)
session['known']=False
else:
session['known']=True
session['name']=form.name.data
form.name.data = ''
return redirect(url_for('index'))
return render_template('index.html',form=form,known=session.get('known', False),name=session.get('name'))
if __name__ == '__main__':
app.run()
index.html中的内容
{% extends 'base.html' %}
{% import 'bootstrap/wtf.html' as wtf %}
{% block title %}Flasky{% endblock %}
{% block page_content %}
<div class="page-header">
<h1>Hello,{% if name %}{{name}}{% else %}Stranger{% endif %}!</h1>
{% if not known %}
<p>Pleased to meet you !</p>
{% else %}
<p>Happy to see you again!</p>
{% endif %}
</div>
{{ wtf.quick_form(form) }}
{% endblock %}
运行
from sql import db
>>> db.create_all()
在数据库中会出现roles
和users
这两个表,当提交的名字是数据库中不存在的,会出现Pleased to meet you !
并储存在users表中,若是再输入一次名字或者是数据库中有的,则出现Happy to see you again!
session
flask这里并不包含数据库操作的框架,就只能将session存储在cookie中。因为cookie实际上是存储在客户端(浏览器)中的,所以称之为客户端session
序列化session的主要过程
- json.dumps 将对象转换成json字符串,作为数据
- 如果数据压缩后长度更短,则用zlib库进行压缩
- 将数据用base64编码
- 通过hmac算法计算数据的签名,将签名附在数据后,用
.
分割
第4步就解决了用户篡改session的问题,因为在不知道secret_key的情况下,是无法伪造签名的。最后,在cookie中就能看到设置好的session了。