常用快捷键:
????????自动导包:option+enter(mac)ctl+enter(windows)
? ? ? ? 查看传参:ctr+ p
? ? ? ? 新增文件:ctr + n
1、hello_world
from flask import Flask
# 创建应用对象
# __name__:如果程序直接在当前文件执行,值为__main__,如果从其他文件调用执行,表示模块名称
# static_url_path:静态资源路径默认为:./static
#template_folder: 模板路径,默认为:./templates
app = Flask(__name__)
# 使用路由路径,绑定视图函数
@app.route('/')
def hello_world():
return 'hello world!'
if __name__ == '__main__':
app.run()
这是flask框架最简单的一段代码,以及其中参数的含义
1、如果一个路由装饰多个函数,那么访问的是哪个函数?
????????访问先被装饰的函数,相当于一个队列,先进先出
2、如果一个视图函数由多个路由装饰,那么优先访问哪个路径?
????????所有的路径都能访问视图函数
2、methods
若一个路由装饰多个函数,可以通过methods,确认请求方式来确认访问的视图函数.methods 是一个列表,不同请求方式使用逗号分割
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'hello world!'
@app.route('/', methods=["post"])
def hello_world2():
return 'hello world2!'
if __name__ == '__main__':
app.run()
3、url_for
url_for 是属于app的方法,在flask模块中
格式:url_for('试图函数名字', key=value)
可以通过视图函数找到对应的路由路径,用于服务器内部资源定位,常与redirect连用
使用url_for传递参数时,接受的参数名需要与传递的参数名一致
from flask import Flask, url_for
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'hello world<a href={}>点击按钮</a>'.format(url_for('hello_world2'))
@app.route('/index')
def hello_world2():
return 'hello world2!'
if __name__ == '__main__':
app.run()
传参:
from flask import Flask, url_for
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'hello world<a href={}>点击按钮</a>'.format(url_for('hello_world2', money=100))
@app.route('/index/<int:money>')
def hello_world2(money):
return 'hello world2!,钱数:{}'.format(money)
if __name__ == '__main__':
app.run()
4、redirect
重定向,可以通过路由找到视图函数,并自动跳转执行
格式:redirect('路径'),常与url_for一起使用
from flask import Flask, url_for, redirect
app = Flask(__name__)
@app.route('/')
def hello_world():
return redirect(url_for('hello_world2', money=100))
@app.route('/index/<int:money>')
def hello_world2(money):
return 'hello world2!,钱数:{}'.format(money)
if __name__ == '__main__':
app.run()
5、返回json数据
有两种方式:
a:通过jsonify(dict),flask模块中
b:json.dump() ,需要配合response对象设置,不推荐
from flask import Flask, url_for, redirect, jsonify
app = Flask(__name__)
@app.route('/')
def hello_world():
dict={
"name":"zhangsan",
"age":19
}
return jsonify(dict)
if __name__ == '__main__':
app.run()
在浏览器控制台可以看到响应头的conten-type是json格式
使用json.dumps()
from flask import Flask, url_for, redirect, jsonify, json, make_response
app = Flask(__name__)
@app.route('/')
def hello_world():
dict={
"name":"zhangsan",
"age":19
}
return json.dumps(dict)
if __name__ == '__main__':
app.run()
上面代码在浏览器返回的是text格式,若要返回json格式,还需要借助response对象
6、路由传参
格式:app.route("路径/<数据类型:变量名>")
数据类型:
? ? ? ? int:整数
? ? ? ? float:小数
? ? ? ? path:字符串
from flask import Flask, url_for, redirect, jsonify
app = Flask(__name__)
@app.route('/<path:reasion>')
def hello_world(reasion):
return "我今天是因为{}迟到了!".format(reasion)
if __name__ == '__main__':
app.run()
但是没办法限制传参的个数,可以通过自定义转换器
之所以可以编写int、float、path是因为系统已经添加默认的转换器,如果要限制参数的具体个数等类型需要自定义
a、自定义类,继承自BaseConverter
b、重写__init__方法,接受两个参数,调用父类__init__方法初始化父类内容
c、将规则赋值到子类对象
d、将转换器添加到系统默认的转换器列表中
查看系统自带的转换器:app.url_map.converters
from flask import Flask, url_for, redirect, jsonify
from werkzeug.routing import BaseConverter
app = Flask(__name__)
class MyConvert(BaseConverter):
def __init__(self, url_map, regex):
super(MyConvert, self).__init__(url_map)
self.regex = regex
app.url_map.converters['re'] = MyConvert
@app.route('/<re("1[35768]\d{9}"):number>')
def hello_world(number):
return "the phonenumber is {}".format(number)
if __name__ == '__main__':
app.run()
?to_python 方法
from flask import Flask, url_for, redirect, jsonify
from werkzeug.routing import BaseConverter
app = Flask(__name__)
class MyConvert(BaseConverter):
def __init__(self, url_map, regex):
super(MyConvert, self).__init__(url_map)
self.regex = regex
# 重写to_python 方法
def to_python(self, value):
return 'xixi' + value
app.url_map.converters['re'] = MyConvert
@app.route('/<re("1[35768]\d{9}"):number>')
def hello_world(number):
return "the phonenumber is {}".format(number)
if __name__ == '__main__':
app.run()
请求结果
在匹配完规则后,进入到视图函数之前执行该方法,可以过滤数据或编码操作
7、调试模式
在运行程序的时候,可以通过app.run(debug=True)进入调试模式,修改代码之后,保存,自动重新启动
好处:
? ? ? ? a、程序改动之后,不需要重新启动既可以部署
? ? ? ? b、如果程序报错,有友好提示界面? ? ? ??
if __name__ == '__main__':
app.run(debug=True)
8、abort
主动停止程序,会配合@app.errorhandler使用,用来自定义错误界面
from flask import Flask, abort
app = Flask(__name__)
@app.route('/')
def hello_world():
abort(403)
return "hello world!!"
常与@app.errorhandler装饰器使用,可以自定义错误
from flask import Flask, abort
app = Flask(__name__)
@app.route('/')
def hello_world():
abort(404)
return "hello world!!"
@app.errorhandler(404)
def exception_404(e):
return "找不到资源"
if __name__ == '__main__':
app.run(debug=True)
##########################################################
from flask import Flask, abort
app = Flask(__name__)
@app.route('/')
def hello_world():
# abort(404)
raise(Exception("big error"))
return "hello world!!"
@app.errorhandler(404)
def exception_404(e):
return "找不到资源"
@app.errorhandler(Exception)
def exception(e):
return "出现大错误"
if __name__ == '__main__':
app.run(debug=True)
9、请求对象和返回对象
request:和请求对象相关,里面封装了请求有关的信息,比如请求方式、请求路径、请求参数
from flask import Flask, request
app = Flask(__name__)
@app.route('/')
def hello_world():
print(request.method)
print(request.url)
print(request.args)
return "hello world!"
if __name__ == '__main__':
app.run(debug=True)
###################################
# 响应:
# 127.0.0.1 - - [02/May/2022 13:26:02] "GET
# http://127.0.0.1:5000/?name=zhagnsan&age=13
# ImmutableMultiDict([('name', 'zhagnsan'), ('age', '13')])
# GET /?name=zhagnsan&age=13 HTTP/1.1" 200 -
响应对象,在后端返回数据的时候,有两种主要方式
直接返回
? ? ? ? 1、响应体? ? ? ? return “hello world”
? ? ? ? 2、响应体+状态码? ? ? ? return? “hello world”,666
? ? ? ? 3、响应体+状态码+响应头????????return? “hello world”,666,{"Content-Type":"application/json"}
手动封装response对象返回
response = make_response(响应体)
response.status = "666"
response.hearder["Content-Type"] =?"application/json"
10、请求勾子
在客户端和服务器交互的过程中,有些准备工作或扫尾工作需要处理,比如:
- 在请求开始时,建立数据库连接;
- 在请求开始时,根据需求进行权限校验;
- 在请求结束时,指定数据的交互格式;
请求钩子是通过装饰器的形式实现,Flask支持如下四种请求钩子
before_first_request:在处理第一个请求前执行
before_request:每次请求前执行,如果在某修饰的函数中返回了一个响应,视图函数将不再被调用
after_request:如果没有抛出错误,在每次请求后执行;接受一个参数:视图函数作出的响应;在此函数中可以对响应值在返回之前做最后一步修改处理;需要将参数中的响应在此参数中进行返回
teardown_request:每次请求后执行,接受一个参数:错误信息,如果有相关错误抛出
11、cookie和session
http 是一种无状态协议,浏览器请求服务器是无状态的,有时需要保持下来用户浏览的状态,比如用户是否登录过,浏览过哪些商品等
- 在客户端存储信息使用
Cookie - 在服务器端存储信息使用
Session
设置cookie:当前浏览器访问服务器时,由服务器设置一些状态信息到浏览器中,存储在浏览器中
? ? ? ? response.set_cookie(key,value,max_age)? ? ? ? max_age: 过期时间
获取cookie:
? ? ? ? request.cookies.get(key)
from flask import Flask, make_response, request
app = Flask(__name__)
@app.route('/set_cookie')
def set_cookie():
response = make_response()
response.set_cookie('name','zhangsan')
response.set_cookie('age',"13", 10)
return response
@app.route('/get_cookie')
def get_cookie():
name = request.cookies.get('name')
age = request.cookies.get('age')
return "name is {}, age is {}".format(name, age)
if __name__ == '__main__':
app.run(debug=True)
设置cookie
获取cookie
session: 当浏览器访问服务器的时候,由服务器创建并存储在服务器内部,依赖于cookie
? ? ? ? 一般用来存储用户的隐私数据,比如:密码信息,登录信息,银行卡信息等
seesion数据设置过程:
? ? ? ? 1、当第一次请求的时候,在服务器端会给客户端设置响应的session数据,返回给客户端,存储在cookie中
? ? ? ? 2、当第二次发送请求是,cookie中会携带sessionID到服务器,通过ID可以获取到session中的数据
? ? ? ? 每一个用户向服务器请求的时候都会给其开辟对应的空间保存session数据
设置session:
? ? ? ? session[key] = value?
? ? ? ? 设置session的编号生成依赖于SECRE_KEY
获取session:
? ? ? ? session.get(key)? 若为空,直接返回空,推荐
? ? ? ? session[key]? ? ? ? 若为空,报错。不推荐
from flask import Flask, session
app = Flask(__name__)
app.config['SECRET_KEY'] = 'jhfjafjkd'
@app.route('/set_session/<name>')
def set_session(name):
session['name'] = name
return '设置session成功'
@app.route('/get_session')
def get_session():
se = session.get('name')
return 'session is {}'.format(se)
if __name__ == '__main__':
app.run(debug=True)
?12、flask_script 动态指定IP和端口
在时机开发中,项目封板之后,如果部署的服务器发生变化,意味着IP和端口可能出现变化,如果直接修改源代码风险比较大,而且繁琐,可以通过flask_script解决,可以动态指定运行的IP和端口
安装:
pip install flask-script
创建Manager对象,关联app应用程序对象
运行:python xxx.py runserver -h 127.0.0.1 -p 8888
from flask import Flask
from flask_script import Manager
app = Flask(__name__)
# 设置调试模式
app.config["DEBUG"] = True
# 创建manager管理对象,关联app应用程序对象
manager = Manager(app)
@app.route('/')
def hello_world():
return "hello world"
if __name__ == '__main__':
# app.run(debug=True)
manager.run()
13、模板渲染
Flask中页面的渲染是通过jinja2模块来实现的,依赖于render_template函数
模板中的注释:{# #}
我们在编写模板时,变量都是没有提示的,设置步骤:
使用格式:
? ? ? ? return render_temperate('模板文件',key=value)
? ? ? ? 将HTML文件渲染撑响应体对象,可以携带value值到页面中
模板的默认位置:templates,若没有该文件,报错
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def hello_world():
return render_template('file01.html')
if __name__ == '__main__':
app.run(debug=True)
模板中的常见语法? ? ? ??
? ? ? ? 获取变量:{{变量名称}}
? ? ? ? 分支语句:{% if 条件A%}
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 语句1
? ? ? ? ? ? ? ? ? ? ? ? ? ?{%elif 条件B%}
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 语句2
? ? ? ? ? ? ? ? ? ? ? ? ? ?{%endif%}
? ? ? ? 循环语句:
? ? ? ? ? ? ? ? {%for 变量 in 容器%}
? ? ? ? ? ? ? ? ? ? ? ? 语句
? ? ? ? ? ? ? ? ?{%endfor%}
在HTML文件中若元祖或者列表取值时,可以使用list.0或者list[0],若是字典,可以dict.key
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def hello_world():
num = 3
str = '你好'
tuple = (10, 11, 12, 13)
list = [1, 2, 3, 4]
dict = {
"name":"张三",
"age":17
}
return render_template('file01.html', num=num, str=str, tuple= tuple, list=list, dict=dict)
if __name__ == '__main__':
app.run(debug=True)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>this is a template</h1>
<h2>整数:{{ num }}</h2>
<h2>字符串:{{ str }}</h2>
<h2>元祖:{{ tuple }}, 分开获取:{{ tuple.0 }}, {{ tuple[2] }}</h2>
<h2>列表:{{ list }}, 分开获取: {{ list.0 }}, {{ list[1] }}</h2>
<h2>字典:{{ dict }}, 分开获取: {{ dict.name }}, {{ dict.age }}</h2>
<h2>遍历列表中的偶数</h2>
{% for n in list %}
{% if n %2==0 %}
<h3>{{ n }}</h3>
{% endif %}
{% endfor %}
<h2>获取元祖的编号</h2>
{% for num in tuple %}
{# <h3>索引:{{ loop.index0 }}, 值是:{{ num }}</h3> index0 表示从0开始,若是index,表示从1开始#}
<h3>索引:{{ loop.index0 }}, 值是:{{ num }}</h3>
{% endfor %}
<h2>遍历字典</h2>
{% for key in dict %}
{# <h3>{{ key }} = {{ dict[key] }}</h3> 不能通过dict.key来取值,这表示dict中有个主键是key,此时key不是一个变量#}
<h3>{{ key }} = {{ dict[key] }}</h3>
{% endfor %}
</body>
</html>
过滤器
常见的过滤器有两种:
字符串过滤器:
upper:将字符串转成大写
lower:将字符串转成小写
reverse:将字符串反转
2、列表过滤器
first:第一个元素
last:最后一个元素
sum:取和
sort:排序
使用方法:变量名|过滤器
14、模板复用
宏:提前定义好模板代码块需要的时候调用即可
定义:{ %? macro 宏名(参数)? ?% }
? ? ? ? ??{ %? endmacro? ?% }
{% macro input(name,value='',type='text') %}
<input type="{{type}}" name="{{name}}"
value="{{value}}" class="form-control">
{% endmacro %}
使用:
? ? ? ? 当前文件:{{宏名(参数)}}
? ? ? ? 其他文件:{ %import ”宏所在的文件“ as 别名% }
? ? ? ? ? ? ? ? ? ? ? ? ? {% 别名.宏名(参数)?%}
模板继承是为了重用模板中的公共内容,共性抽取,代码复用,继承之后可以扩展子模板独有的内容
{%extends "文件名"%}
包含:A完全用友B内容,一般对于不需要改变的内容使用包含
{%include??"文件名" ignore missing%}? ? ? ? #ignore missing如果文件不存在,不报错
模块的定义:
{%block 名称%}
{%endblock%}
继承
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% extends 'file03fu.html' %}
</body>
</html>
15、flask表单
传统表单:有大量的HTML定义好的标签组成
缺点:一旦表单编写完成,如果需要增加额外的功能,就不能实现
传统表单
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/register" method="post">
<label>用户名</label><input type="text" name="username" placeholder="请输入用户名"><br>
<label>密码</label><input type="text" name="password" placeholder="请输入密码"><br>
<label>确认密码</label><input type="text" name="repassword" placeholder="请输入确认密码"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
from flask import Flask, render_template, request
app = Flask(__name__)
@app.route('/')
def hello_world():
return render_template('file04nom.html')
@app.route('/register', methods=["post"])
def register():
print(request.form)
usrname =request.form.get('username')
password =request.form.get('password')
repassword =request.form.get('repassword')
if not all([usrname, password, repassword]):
return '没有填写必要内容'
if password != repassword:
return "密码不一致"
return '注册成功'
if __name__ == '__main__':
app.run(debug=True)
Flask_WTF:通过类的方式渲染页面,包含字段和验证函数
步骤:
- 安装,导包
- 自定义类,继承自FlaskForm
- 编写字段,验证函数
- 创建表单对象
- 渲染到页面中
包含csrf验证机制
from flask import Flask, render_template
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField, PasswordField
from wtforms.validators import DataRequired, EqualTo
app = Flask(__name__)
app.config["SECRET_KEY"] = 'yhfjnfhhf'
# 自定义类,继承自FlaskForm
class MyForm(FlaskForm):
username = StringField('用户名', validators=[DataRequired()])
password = PasswordField('密码', validators=[DataRequired()])
repassword = PasswordField('确认密码', validators=[DataRequired(), EqualTo('password', '两次输入的密码必须一致!')])
submit = SubmitField('提交')
@app.route('/')
def hello_world():
# 创建表单
form = MyForm()
return render_template('file05wtf.html', form=form) #将form对象传递到模板文件中
@app.route('/register', methods=["post"])
def register():
# 根据创建的内容创建表单
form = MyForm()
# 验证提交的内容
if form.validate_on_submit():
return "注册成功"
return "注册失败"
if __name__ == '__main__':
app.run(debug=True)
HTML文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/register" method="post">
{{ form.csrf_token }}
{{ form.username.label }}
{{ form.username }}<br>
{{ form.password.label }}
{{ form.password }}<br>
{{ form.repassword.label }}
{{ form.repassword }}<br>
{{ form.submit }}
</form>
</body>
</html>
16、数据库
常用的数据库命令:
# 进入mysql,回车输入密码
mysql -uroot -p
# 退出登录
quit
exit
ctrl+d
# 查看所有数据库
show databases;
# 使用数据库
use 数据库名;
# 删除数据库
drop 数据库名;
# 查看当前数据库中的所有表
show tables;
# 删除表
drop table 表名;
# 数据库备份
mysqldump -uroot -p 数据库名 > python.sql
# 数据库恢复
mysql -uroot -p 数据库名 < python.sql
flask操作数据库是通过库战报Flask_Sqlalchemy
操作流程:
? ? ? ? ? ? ? ? pip install flask-sqlalchemy
????????????????pip install flask-mysqldb? ?若报错可以安装pip install pymsql
? ? ? ? ? ? ? ? <协议mysql+pymsql>://<用户名>:<密码>@<IP地址>:<端口号>/<数据库名称>
? ? ? ? ? ? ? ? 如果安装的是mysqld,协议使用mysql;如果安装的是pymysql,协议使用mysql+pymysql
- 创建ORM对象SQLALchemy,关联app
- 定义模型类
- 操作(方法)
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
# 设置数据库信息
app.config['SQLALCHEMY_DATABASE_URI'] = "mysql+pymysql://root:123456@127.0.0.1:3306/pytest"
# 设置数据库的追踪数据变化,并压制警告信息
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
#创建SQLAlchemy对象
db = SQLAlchemy(app)
# 定义模型类 角色模型
class Role(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True, nullable=False)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True, nullable=False)
# 外键
role_id = db.Column(db.Integer, db.ForeignKey(Role.id))
@app.route('/')
def hello_world():
return "hello world"
if __name__ == '__main__':
db.create_all() # 将继承自db.Model全部新增
#db.drop_all() 会将继承自db.Model的表全部删掉
#db.session.add(obj) 添加数据,提交回话后才会插入到数据库
#db.session.delete(obj) 删除数据,提交回话后才会更新数据库
#db.session.commit() 提交会话
#db.session.rollback() 回滚
# 添加多条
#us9 = User(name='li',email='li@163.com',password='4526342',role_id=ro2.id)
#us10 = User(name='sun',email='sun@163.com',password='235523',role_id=ro2.id)
#db.session.add_all([us9,us10]) 添加的是一个列表
#db.session.commit()
app.run(debug=True)
执行成功后,数据库会新增两个表,role表和name表,若没有指定表名称,默认是类名的小写,在类中设置__tablename__=="表名"即可
User.query.all() #查询所有用户数据
User.query.count() #查询有多少个用户
User.query.filter_by(name='wang').all() # 精确查询,通过name查询
User.query.first() #查询第1个用户
#查询id为4的用户
User.query.filter_by(id=4).first()
User.query.get(4)
User.query.filter(id==4).first() # filter用于模糊查询
#查询名字结尾字符为g的所有数据[开始/包含]
User.query.filter(User.name.endswith('g')).all()
User.query.filter(User.name.startswith('g')).all() # 以g开头的
User.query.filter(User.name.contains('g')).all() # 包含g
#查询名字不等于wang的所有数据
User.query.filter(User.name != 'wang').all()
#查询名字和邮箱都以 li 开头的所有数据
User.query.filter(User.name.startswith('li'),User.email.startswith('li'))
#查询password是 `123456` 或者 `email` 以 `itheima.com` 结尾的所有数据
from sqlalchemy import or_
User.query.filter(or_(User.password=='123456', User.email.endswith('itheima.com'))).all()
#查询id为 [1, 3, 5, 7, 9] 的用户列表
from sqlalchemy import in_
User.query.filter_by(User.id.in_([1, 3, 5, 7, 9])).all()
# 查询name为liu的角色数据
user = User.query.filter_by(name="liu").first()
role = Role.query.filter(Role.id == user.role_id).first()
# 查询所有用户数据,并以邮箱排序
User.query.order_by(User.email).all() #默认升序
User.query.order_by(User.email.desc()).all() #降序返回
#每页3个,查询第2页的数据
paginate = User.query.paginate(2, 3, False) # False 有错误不输出
paginate.pages # 返回页数
paginate.items # 显示第二页的所有数据
#查询数据后删除
user = User.query.first()
db.session.delete(user)
db.session.commit()
#更新数据
user = User.query.first()
user.name = 'dong'
db.session.commit()
17、backref 反向引用
数据库查询操作:
- 如果知道了用户对象,能不能快速查询出该用户所扮演的角色。例如:user.role
- 如果知道了角色对象,能不能快速查询出哪些用户扮演了该角色。例如:role.user
使用relationship关系属性,设置backref反向引用,一般建立在一方,外键在多方 一对多:A表中的一条数据对应B表中的一条数据,B表中的一条数据对应A表中的多条数据,此时A与B是多对一的关系
In [1]: from test_query import *
In [2]: user = User.query.filter(User.name=='liu').first()
In [3]: user.role
Out[3]: <role:1,admin>
In [2]: admin = Role.query.filter(Role.name=='admin').first()
In [3]: admin
Out[3]: <role:admin>
In [4]: admin.users
Out[4]:
[<user:wang,wang@163.com,123456>,
<user:zhou,zhou@163.com,456789>,
<user:qian,qian@gmail.com,1543567>,
<user:liu,liu@itheima.com,867322>]
|