1.工程搭建:
(1) 安装flask: pip3 install flask
(2)命令行:
(1)终端运行:flask run
(2)绑定IP地址和端口:Flask run -h 127.0.0.1 -p 8083 修改端口号
- (3)启动项目: pyhthon app.py
- (4) 设置真实项目:FLASK_APP = "RealProject"
- (5)生产环境:set FLASK_ENV=production
- (6)开发模式:set FLASK_ENV=development
注意:虽然 flask 命令可以方便地启动一个本地开发服务器,但是每次应用代码 修改之后都需要手动重启服务器。这样不是很方便, Flask 可以做得更好。如果你打开 调试模式,那么服务器会在修改应用代码之后自动重启,并且当应用出错时还会提供一个 有用的调试器。在命令行中,如果需要打开所有开发功能(包括调试模式),那么要在运行服务器之前导出 FLASK_ENV 环境变量并把其设置为 development:
2. flask框架的路由:
terminal中输入显示所有的路由:flask routes
(1) 路由定义
from flask import Flask
app = Flask(__name__)
"""
路由设置的注解路径: /index
"""
@app.route('/index',methods=['POST','GET'])
def hello_world():
return 'Hello World!'
"""
路由的路径不能相同,每个函数对应不同的路由;不同的功能;
"""
@app.route('/return')
def return_content():
return '你好!'
"""
启动本地服务:
"""
if __name__ == '__main__':
'设置ip 和端口号'
app.run(debug=True,host='127.0.0.1',port=8083)
(2)路由中传递参数:
除了默认字符串变量部分之外,还可以使用以下转换器构建规则:
转换器 | 描述 |
---|---|
string | (缺省值) 接受任何不包含斜杠的文本 |
int | 接受正整数 |
float | 接受正浮点数 |
path | 类似 string ,但可以包含斜杠 |
uuid | 接受 UUID 字符串 |
#在路由中传递参数
@app.route('/get_name/<name>',methods=['POST','GET'])
def get_params(name):
return '你好!' + name
(3)request 对象的使用:
1)HTTP请求方法:
GET | 以未加密的形式将数据发送到服务器,最常见的方法。 |
HEAD | 和GET方法相同,但没有响应体。 |
POST | 用于将HTML表单数据发送到服务器,POST方法接收的数据不由服务器缓存。 |
PUT | 用上传的内容替换目标资源的所有当前表示。 |
DELETE | 删除由URL给出的目标资源的所有当前表示。 |
2)参数格式:
data(请求的数据,类型为string)、
form(表单的数据)、
args(记录请求中的查询参数?号后面的参数)
headers
method(get\post)
cookies
url
files:文件类型;上传图片)
(4)响应对象:response:
1)返回模板:render_template() :返回一个模板文件
2)重定向redirect: 跳转到其他的路由
3)返回json数据: ( json.load() :读json 文件)
json.dumps() :写json文件;
jsonify() :返回为json,同时设置响应头;
4)Cookie使用: 存储在客户端,显示用户的访问记录
一般使用response对象.set_cookie() 来设置cookie
得到cookie: request.Cookies.get("XXX") 得到cookie
5)Session使用:保存前端提交的状态的数据;存放在浏览器服务器上;
1)设置session:
2.得到session:session.get() :
3删除session:
①删除单条session值,可以采用session.pop('name')
方法;
②清空整个session内容,则采用session.clear()
方法;
6)abort函数的使用:异常处理函数;
- 使用类似于python中的raise函数,可以在需要退出请求的地方抛出错误,并结束该请求;
- 我们可以使用app.erroehandler装饰器来进行异常的捕获与自定义:
from flask import Flask,render_template,request,abort
app = Flask(__name__)
@app.route("/",methods=['GET','POST'])
def index():
if request.method == 'GET':
return render_template('index.html')
elif request.method == 'POST':
name = request.form.get('name')
password = request.form.get('password')
if name == 'zhangsan' and password == '123456':
return 'login sucess'
else:
# abort的用法类似于python中的raise,在网页中主动抛出错误
abort(404)
return None
# 自定义错误处理方法,将404这个error与Python函数绑定
# 当需要抛出404error时,将会访问下面的代码
@app.errorhandler(404)
def handle_404_error(err):
# return "发生了错误,错误情况是:%s"%err
# 自定义一个界面
return render_template('404.html')
if __name__ == '__main__':
app.run()
7)url_for实现反转:
url_for是实现url反转的工具,即视图函数→url;
静态文件引入:url_for('static', filename='文件路径')
(5)请求钩子:
中间件(中间件对所有的view都起作用:):是一个类,然后定义pre-process、after-process方法;
请求处理的过程:pro-process----view -----after-process------>(return response)
请求钩子的类别:
before_request
:在每一次请求之前调用;before_first_request
:与before_request的区别是,只在第一次请求之前调用;只调用一次after_request
:每一次请求之后都会调用;teardown_request
:每一次请求之后都会调用;
请求钩子执行顺序:
before-first-request-----before-request----after-request-------teardown-request(接近结尾处理)
3. flask框架的视图:
(1) add_url_rule的初登场
欲实现url与视图函数的绑定,除了使用路由装饰器@app.route,我们还可以通过add_url_rule(rule,endpoint=None,view_func=None)
方法
rule:设置的url
endpoint:给url设置的名称
view_func:指定视图函数的名称
(2) 视图函数和类视图:
类视图的引入
之前我们所定义的视图都是通过函数来实现的,所以称之为视图函数,但其实视图还可以由类来实现,即类视图;
标准类视图:
定义时需要继承flask的views.View这一基类;
每个类视图内必须包含一个dispatch_request方法,每当类视图接收到请求时都会执行该方法,返回值的设定和视图函数相同;
区别:
视图函数可以通过@app.route和app.add_url_rule来进行注册(映射到url),但类视图只能通过app.add_url_rule来注册,注册时view_func不能直接使用类名,需要调用基类中的as_view方法来为自己取一个“视图函数名”
from flask import Flask,render_template,views
app = Flask(__name__)
# 定义父视图类继承基类View
class Ads(views.View):
def __init__(self):
super(Ads, self).__init__()
# 实例属性
self.context={
'ads':'这是对联广告!'
}
# 定义子视图类继承父类并实现工程
class Index(Ads):
def dispatch_request(self):
# 字典传参方式==不定长的关键字传参
return render_template('class_mould/index.html',**self.context)
class Login(Ads):
def dispatch_request(self):
# 字典传参方式==不定长的关键字传参
return render_template('class_mould/login.html',**self.context)
class Register(Ads):
def dispatch_request(self):
# 字典传参方式==不定长的关键字传参
return render_template('class_mould/register.html',**self.context)
# 注册我们创建的类视图,as_view给类视图起名
app.add_url_rule(rule='/',endpoint='index',view_func=Index.as_view('index'))
app.add_url_rule(rule='/login/',endpoint='login',view_func=Login.as_view('login'))
app.add_url_rule(rule='/register/',endpoint='register',view_func=Register.as_view('register'))
if __name__=='__main__':
print(app.view_functions)
app.run(debug=True)
(3) 蓝图的使用:
一个蓝图就是一个模块;视为一种容器对象;有自己的模板文件、静态文件等;
蓝图使用方式:创建蓝图和注册蓝图。
蓝图内部的静态文件: static_folder:静态文件夹
4. jinja2模板引擎:
(1) 模板的使用:
- 后端传数据:Flask通过render_template来实现模板的渲染,要使用这个方法,我们需要导入from flask import rander_template,模板中注释需放在{# #}中
- 模板的第一个参数为指定的模板文件名称,如自定义的html文件,第二个或者更多参数为可选项,用于向模板中传递变量。
- 如果有多个变量需要传递,我们可以不需要一个一个进行传参,直接使用
**locals()
替代我们在当前视图函数中定义的所有变量:
from flask import Flask,render_template
app = Flask(__name__)
# 给前端模板传参
@app.route("/")
def index():
data = {
'name':'张三',
'age':18,
'mylist':[1,2,3,4,5,6,7]
}
# 以键值对的形式传参给模板index2.html
# 左边是形参:data);
# 右边是我们给这个变量传的值(实参:字典data);
return render_template('index2.html',data=data)
if __name__ == '__main__':
app.run()
前端接受数据:
前端html模板内需要在双括号{{ }}中使用该变量:
如果想给该变量添加属性便于CSS修改格式,我们可以在变量后添加括号,并在括号内定义class、id等属性
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
hello world
<br>
<!-- 对传入变量的使用并显示:在双括号内,和python中用法类似 -->
{{ data }}
<br>
{{ data['name'] }}
<br>
{{ data.name }}
<br>
mylist:{{ data.mylist }}
<br>
mylist[1]:{{ data.mylist[1] }}
<br>
count:{{ data.mylist[1]+data.mylist[2] }}
</body>
</html>
(2)模板中的控制语句:
-
jinja2模板引擎中也可使用if和for控制语句,但是语句需要放置在
{% %}
中; -
if条件判断语句必须包含结束标签{% endif %},其他部分与python中类似,可以与比较运算符
> >= < <= == !=
结合使用,或与逻辑运算符and,or,not,()
结合使用;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% if name==1 %}
<h1>恭喜你抽中了一等奖!</h1>
{% if name==2 %}
<h1>恭喜你抽中了二等奖!</h1>
{% else %}
<h1>恭喜你抽中了三等奖!</h1>
{% endif %}
</body>
</html>
- for循环控制语句在模板内的用法也和python中类似,遍历的对象可以是字典、元组、列表等,但需要注意的是在模板中无法使用continue和break来对循环进行控制;
{% for 目标 in 对象 %}
<p>目标</p>
{% endfor %}
(3)过滤器的使用与自定义:
- 可以在前端模板内
{{ 内容 | 过滤器 }}
的" | "后使用; - 可以使用
add_template_filter(函数方法名,'过滤器名')
来自定义过滤器;
# 自定义过滤器
def list_step(li):
# 返回列表,步长为2
return li[::2]
# 注册模板过滤器(filter)
# 参数1为该过滤器调用的函数,参数2为在前端中调用该过滤器使用的名称
app.add_template_filter(list_step,'li2')
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!-- 过滤器的使用 -->
<!-- 全大写 -->
{{ 'hello_world' | upper }}
<br>
<!-- 单词首字母大写 -->
{{ 'hello world' | title }}
<br>
<!-- 替换左边的内容为右边的内容 -->
{{ 'hello_world' | replace('hello','hi') }}
<br>
<!-- 调用自定义的过滤器 -->
mylist列表:{{ data.mylist | li2 }}
</body>
</html>
(4) include的使用:
- include用于在一个模板的指定位置导入另一个模板的内容,区别于宏的调用,include更像从另一个模板“复制+粘贴”;
- include同样在
{% %}
中使用,采用语句{% include 模块名 %}
,相当于复制。
(5) 加载静态文件
静态文件一般是我们在开发过程中用到的图片文件、css文件和js文件,在Flask工程中通常包含一个static文件目录,当需要调用静态文件是将会默认在该目录下进行查询,固不需要使用相对路径;
通常我们会在static文件目录下定义名为css、image和js的文件夹分别存储这些静态文件;
加载静态文件通常配合url_for函数使用(需要在双括号内调用),将模板标签的src、herf属性通过url_for(静态文件名称)设置为反转url要比使用相对路径更好。
<head>
<!-- 导入js文件 -->
<script type="text/javascript" src="{{url_for('static',filename='js/jquery-3.5.1/jquery-3.5.1.js')}}"></script>
<!-- 导入css文件 -->
<link rel="stylesheet" href="{{url_for('static',filename='css/car.css')}}">
</head>
<body>
<!-- 导入图片 -->
<img alt="" src="{{ url_for('static',filename='image/car.jpg') }}"/>
</body>
(6)extends继承模板
- 在include中,我们对于当前模板需要插入的代码块,可以在其他模板中定义,然后用include导入进来,外部模块是当前模块的补充,引入新的子模块;
- 而在extends中,我们当前的模板则是待装载的代码块,需要我们继承一个框架来搭载这些代码块,这时候就需要extend来导入框架(基类)模块了;在继承父类的模板基础上,然后生成自己的模板。
- 通常使用{% block xxx %} xxxx {% endblock %}来充当占位符。
5. flask数据交互:
(1) 使用flask传统的处理表单:
1)传统的前端通用表单,需要前后端共同完成操作,前端需要使用form标签来定义表单,而后端则需要使用request.form
来获取post请求中的表单数据:
# 判断请求方式
if request.method == 'POST':
# 获取表单中name为username的文本域提交的数据
name = request.form.get('username')
# 获取表单中name为password的文本域提交的数据
password = request.form.get('password')
return name+" "+password
2)使用第三方组件:flask-wtf 和wtforms 来实现后端独立完成的表单操作;
- wtforms安装:
pip install wtforms
- flask-wtf安装:
pip install Flask-WTF
或pip install flask-wtf
wtforms依照功能类别来说wtforms分别由以下几个类别:
- Forms: 主要用于表单验证、字段定义、HTML生成,并把各种验证流程聚集在一起进行验证。
- Fields: 包含各种类型的字段,主要负责渲染(生成HTML文本域)和数据转换。
- Validator:主要用于验证用户输入的数据的合法性。比如Length验证器可以用于验证输入数据的长度。
- Widgets:html插件,允许使用者在字段中通过该字典自定义html小部件。
- Meta:用于使用者自定义wtforms功能(配置),例如csrf功能开启。
- Extensions:丰富的扩展库,可以与其他框架结合使用,例如django。
(2) flash 闪现的使用:
- 导入:
from flask import flash
; - 后端的使用:
flash("message")
,message为消息内容; - 前端通过遍历
get_flashed_messages()
获取flash消息内容;
(3) Flask实现文件上传(后面补充)
- 对于单文件的上传,主要用到
flask_wtf.file
库下的上传字段类:FileField,以及检验组件:FileRequired和 FileAllowed;
6. flask访问数据库:
(1) Flask-SQLAlchemy数据库:
安装 :
pip3 install Flask-SQLAlchemy
SQLAlchemy是一个基于Python实现的ORM (Object Relational Mapping,对象关系映射)框架。该框架建立在DB API (数据库应用程序接口系统) 之上,使用关系对象映射进行数据库操作。简言之便是将类和对象转换成SQL,然后使用数据API (接口) 执行SQL 并获取执行结果。
它的核心思想于在于将关系数据库表中的记录映射成为对象,以对象的形式展现,程序员可以把对数据库的操作转化为对对象的操作。
1)Flask-SQLALchemy的ORM框架便可以实现将操作数据库转变为操作对象,一个book表被抽象成了一个Book类,一个表中的id、tiltle、publishing_office、isbn、storage_time字段被抽象成一个类的五个属性,而该表的一条数据记录就抽象成该类的一个实例化对象,不用再写烦琐的底层SQL语句了。
(2) 初始化数据库配置:
USERNAME = 'root' #设置登录账号
PASSWORD = '123456' #设置登录密码
HOST = '127.0.0.1' #设置主机地址
PORT = '3306' #设置端口号
DATABASE ='flaskdb' #设置访问的数据库
# 创建URI(统一资源标志符)
'''
SQLALCHEMY_DATABASE_URI的固定格式为:
'{数据库管理系统名}://{登录名}:{密码}@{IP地址}:{端口号}/{数据库名}?charset={编码格式}'
'''
DB_URI = 'mysql://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME,PASSWORD,HOST,PORT,DATABASE)
# 设置数据库的连接URI
SQLALCHEMY_DATABASE_URI = DB_URI
# 设置动态追踪修改,如未设置只会提示警告
SQLALCHEMY_TRACK_MODIFICATIONS = False
# 设置查询时会显示原始SQL语句
SQLALCHEMY_ECHO = True
-
上述配置文件设置完后,在flask程序文件下导入该文件,再用
app.config.from_object
方法导入到flask对象内即可;
(3) 表模型的定义与数据库映射
SQLAlchemy允许我们依据数据库的表结构来构建数据模型,即用python类的方式定义一个表模型,再通过调用create_all()方法,就可以将我们定义的所有表模型全部映射为数据库下的表;
但要注意的是每次运行create_all()这行代码时都会将所有表创建一次,导致重复创建表的问题,可以在前面添加一行drop_all()代码来删除已经创建的表;
创建表名:在用类的方式定义表模型时,使用__tablename__='<表名>'来将字符串对象设为表名,
创建列名:使用<列名>=db.Column()来将Column类的实例对象设为表的字段;我们为字段(列)指定的数据类型和约束条件,作为Column实例化时的参数;
SQLAlchemy创建表了,并可以尝试着进行插入记录的操作SQLAlchemy.session.add()
:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import flask学习.config
from datetime import * # datatime库下的datatime类重名了
app = Flask(__name__)
app.config.from_object(flask学习.config)
# 初始化一个SQLAlchemy对象(该步要在导入config后执行)
# 实例化的同时将与数据库相关的配置读入
db = SQLAlchemy(app)
# 初始化app对象中与数据库相关的配置的设置,防止数据库信息泄露
db.init_app(app)
# 创建表模型类对象
class Book(db.Model):
__tablename__='book'
id = db.Column(db.Integer, primary_key = True,autoincrement = True) #定义id字段
title = db.Column(db.String(50),nullable = False) #定义title字段
publishing_office = db.Column(db.String(100),nullable = False) #定义出版社字段
isbn = db.Column(db.String(100),nullable = False) #定义isbn号字段
storage_time = db.Column(db.DateTime, default = datetime.now) # 入库时间字段
if __name__ == '__main__':
# 删除数据库下的所有上述定义的表,防止重复创建
db.drop_all()
# 将上述定义的所有表对象映射为数据库下的表单(创建表)
db.create_all()
# 向表中插入记录:实例化-插入-提交
book1 = Book(id='001',title='人工智能导论',publishing_office ='高等教育出版社',isbn='9787040479843')
db.session.add(book1)
db.session.commit()
(4)数据的增、删、改、查操作
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import flask学习.config
from datetime import * # datatime库下的datatime类重名了
app = Flask(__name__)
app.config.from_object(flask学习.config)
# 初始化一个SQLAlchemy对象(该步要在导入config后执行)
# 实例化的同时将与数据库相关的配置读入
db = SQLAlchemy(app)
# 初始化app对象中与数据库相关的配置的设置,防止数据库连接泄露
db.init_app(app)
# 创建表模型类对象
class Book(db.Model):
__tablename__='book'
id = db.Column(db.Integer, primary_key = True,autoincrement = True) # 定义id字段
title = db.Column(db.String(50),nullable = False) # 定义title字段
publishing_office = db.Column(db.String(100),nullable = False) # 定义出版社字段
price = db.Column(db.String(30), nullable=False) # 定义price号字段
isbn = db.Column(db.String(50),nullable = False) # 定义isbn号字段
storage_time = db.Column(db.DateTime, default = datetime.now) # 入库时间字段
# 删除数据库下的所有上述定义的表,防止重复创建
db.drop_all()
# 将上述定义的所有表对象映射为数据库下的表单(创建表)
db.create_all()
# 添加数据的路由
@app.route('/add')
def add_record():
book1 = Book(title='Python基础教程(第3版)', publishing_office ='人民邮电出版社', price = '68.30 ', isbn = '9787115474889')
book2= Book(title='Python游戏编程快速上手第4版',publishing_office = '人民邮电出版社', price = '54.50', isbn = '9787115466419')
book3 = Book(title='JSP+Servlet+Tomcat应用开发从零开始学',publishing_office = '清华大学出版社', price = '68.30', isbn = '9787302384496')
db.session.add(book1)
db.session.add(book2)
db.session.add(book3)
# 需要提交事务给数据库
db.session.commit()
return 'add success!'
# 查找数据的路由
@app.route('/query')
def query_record():
# 查找id=1的第一个对象
result = Book.query.filter(Book.id == '1').first()
print(result.title)
# 查找publishing_office=人民邮电出版社的全体对象
result_list = Book.query.filter(Book.publishing_office == '人民邮电出版社').all()
for books in result_list:
print(books.title)
return 'query success!'
# 修改数据的路由
@app.route('/edit')
def edit_record():
# 查找id=1的第一个对象
book1 = Book.query.filter(Book.id == '1').first()
book1.price = 168
# 需要提交事务给数据库
db.session.commit()
return 'edit success!'
# 删除数据的路由
@app.route('/delete')
def delete_record():
# 查找id=9的第一个对象
book2 = Book.query.filter(Book.id == '9').first()
db.session.delete(book2)
# 需要提交事务给数据库
db.session.commit()
return 'delete success!'
if __name__ == '__main__':
app.run(debug=True)
1)数据的添加
- 确保数据库已经建立好后,直接使用
db.session.add(<实例化对象>)
的方法就可以把依据某一个表类实例化的数据对象插入到对应表中; - 这一步操作需要进行事务提交
db.session.commit()
;
2)数据的查找
数据的查找需要通过query.filter(<限制条件>)方法来实现,query继承自db.Model,query.filter返回的是查找到的所有满足限制条件的数据对象组成的列表,当没有限制条件时则返回表中所有记录对应的数据对象组成的列表;
可以使用first()来获取查找到的第一个数据对象,也可以用all()来获取查找到的全部数据对象(一个列表);
3)数据的修改
- 数据的修改操作是基于数据的查找操作实现的,先通过查找记录获取到对应的数据对象,再对该对象的某一个字段进行修改即可;
- 这一步操作需要进行事务提交
db.session.commit()
;
4)数据删除:
- 数据的删除和数据的修改一样,也是基于数据的查找实现的,先通过查找记录获取到对应的数据对象,再调用
db.session.delete(<数据对象>)
方法即可删除该数据对象与表中的记录; - 这一步操作需要进行事务提交
db.session.commit()
;
(5)表模型的关联关系构建
数据库实体型(表)间有3种关联关系:一对一、一对多和多对多
在SQLAlchemy中,为了在表模型间创建上述的三种关联关系,
同样要在一个表模型中通过db.ForeignKey('表模型类.属性')的方式添加外键,绑定另一个表模型的主属性(primary_key);添加外键后,
还需要使用<字段名1> = db.relationship('<多方表名>',backref=db.backref('<字段名2>')[,secondary=<Table对象名>,uselist=False])的方法来正式构建关系。
(6) init_app作用:初始化数据库,但必须要提前传入flask对象。
只要在实例化SQLAlchemy对象的过程中,如果参数app不为空(判断是否传入了flask对象,参数app默认值为None),那么就会调用init_app方法来初始化flask对象中数据库部分的相关配置。这时如果我们提前为flask对象导入了配置文件,init_app就无法覆写我们自定义的配置内容,也就相当于“没有起作用”。