Flask
Flask 初识
1、安装
pip install Flask
2、最小的 Flask 应用
from flask import Flask
# 一个Flask类的对象
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
运行之后浏览器访问 http://127.0.0.1:5000
3、Flask 配置文件
(1) Flask 配置参数
| 参数 | 默认值 | 解释 |
|---|---|---|
| DEBUG | False | 是否开启Debug模式 |
| TESTING | False | 是否开启测试模式 |
| PROPAGATE_EXCEPTIONS | None | 异常传播(是否在控制台打印LOG) 当Debug或者testing开启后,自动为True |
| PRESERVE_CONTEXT_ON_EXCEPTION | None | 一两句话说不清楚,一般不用它 |
| SECRET_KEY | None | 在启用Session的时候,一定要有它 |
| PERMANENT_SESSION_LIFETIME | 31 | Session的生命周期(天)默认31天 |
| USE_X_SENDFILE | False | 是否弃用 x_sendfile |
| LOGGER_NAME | None | 日志记录器的名称 |
| LOGGER_HANDLER_POLICY | always | |
| SERVER_NAME | None | 服务访问域名 |
| APPLICATION_ROOT | None | 项目的完整路径 |
| SESSION_COOKIE_NAME | session | 在cookies中存放session加密字符串的名字 |
| SESSION_COOKIE_DOMAIN | None | 在哪个域名下会产生session记录在cookies中 |
| SESSION_COOKIE_PATH | None | cookies的路径 |
| SESSION_COOKIE_HTTPONLY | True | 控制 cookie 是否应被设置 httponly 的标志 |
| SESSION_COOKIE_SECURE | False | 控制 cookie 是否应被设置安全标志 |
| SESSION_REFRESH_EACH_REQUEST | True | 这个标志控制永久会话如何刷新 |
| MAX_CONTENT_LENGTH | None | 如果设置为字节数, Flask 会拒绝内容长度大于此值的请求进入,并返回一个 413 状态码 |
| SEND_FILE_MAX_AGE_DEFAULT | 12 | hours 默认缓存控制的最大期限 |
| TRAP_BAD_REQUEST_ERRORS | False | |
| TRAP_HTTP_EXCEPTIONS | False | |
| EXPLAIN_TEMPLATE_LOADING | False | |
| PREFERRED_URL_SCHEME | http | 生成URL的时候如果没有可用的 URL 模式话将使用这个值 |
| JSON_AS_ASCII | True | |
| JSON_SORT_KEYS | True | |
| JSONIFY_PRETTYPRINT_REGULAR | True | |
| JSONIFY_MIMETYPE | application/json | |
| TEMPLATES_AUTO_RELOAD | None |
(2) from_object()
settings.py
# 基础配置
class BaseConfig(object):
DEBUG = False
TESTING = False
DATABASES_URL = 'sqlite://memory:'
SECRET_KEY = "fiu%&*&TSHWDG^(Y*@HUBCG*&H(*#@*&H(*WH(*&gbuuy)"
# 生产环境
class ProductionConfig(BaseConfig):
DATABASES_URL = "mysql://root@pro/foo"
# 开发环境
class DevelopmentConfig(BaseConfig):
DATABASES_URL = "mysql://root@dev/foo"
DEBUG = True
# 测试环境
class TestingConfig(BaseConfig):
DATABASES_URL = "mysql://root@test/foo"
DEBUG = True
app.py
from flask import Flask, render_template, redirect
app = Flask(__name__)
app.config.from_object('settings.DevelopmentConfig')
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
(3) from_pyfile()
3-1 settings.py
3-2 app.py
from flask import Flask, render_template, redirect
app = Flask(__name__)
app.config.from_pyfile("python文件名称")
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
(4) from_envvar()
app.py
from flask import Flask, render_template, redirect
app = Flask(__name__)
app.config.from_envvar("环境变量名称")
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
(5) from_json()
settings.json
app.py
from flask import Flask, render_template, redirect
app = Flask(__name__)
app.config.from_json("json文件名称")
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
(6) from_mapping()
app.py
from flask import Flask, render_template, redirect
app = Flask(__name__)
app.config.from_mapping({'DEBUG':True})
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
(7) 其他方式
from flask import Flask, render_template, redirect
app = Flask(__name__)
app.config['DEBUG'] = True
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
Flask 路由
@app.route('/user/<username>')
@app.route('/post/<int:post_id>')
@app.route('/post/<float:post_id>')
@app.route('/post/<path:path>')
@app.route('/login', methods=['GET', 'POST'])
# 常用路由系统有以上五种,所有的路由系统都是基于一下对应关系来处理:
DEFAULT_CONVERTERS = {
'default': UnicodeConverter,
'string': UnicodeConverter,
'any': AnyConverter,
'path': PathConverter,
'int': IntegerConverter,
'float': FloatConverter,
'uuid': UUIDConverter,
}
1、注册路由
(1) 方式一
from flask import Flask, render_template, redirect
app = Flask(__name__)
@app.route('/', methods=['GET', 'POST'])
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
(2) 方式二
from flask import Flask, render_template, redirect
app = Flask(__name__)
# 方式二:
def order():
return 'Order'
app.add_url_rule('/order', view_func=order)
if __name__ == '__main__':
app.run()
(3) 方式三
from flask import Flask, views, jsonify
app = Flask(__name__)
class Login(views.MethodView):
# 指定请求方法,这里可以省略不写,根据请求方法自动找到对应的方法去执行
methods = ["GET", "POST"]
# 对类中的函数都加上装饰器,列表中可以放多个装饰器函数名,依次执行
decorators = []
def get(self):
response_data = {"code": 200, "msg": "我是Login视图的GET方法", "data": None}
return jsonify(response_data)
def post(self):
response_data = {"code": 200, "msg": "我是Login视图的POST方法", "data": None}
return jsonify(response_data)
# name其实就是用来填充endpoint的,如果endpoint有值那么name就没有用,且name这个参数是必填的,相当于别名
app.add_url_rule("/login", endpoint=None, view_func=Login.as_view(name="login"))
app.run("0.0.0.0", 9527)
2、反向生成url
(1) FBV
from flask import Flask, render_template, redirect, url_for
app = Flask(__name__)
@app.route('/login', methods=['GET', 'POST'], endpoint='u1')
def login():
return 'login'
@app.route('/loginout', methods=['GET', 'POST'])
def loginout():
return 'loginout'
@app.route('/index', methods=['GET', 'POST'])
def index():
v1 = url_for('u1')
v2 = url_for('loginout')
v3 = url_for('index')
print(v1, v2, v3)
return 'index'
if __name__ == '__main__':
app.run()
(2) CBV
from flask import Flask, views, jsonify, url_for
app = Flask(__name__)
class Login(views.MethodView):
# 指定请求方法,这里可以省略不写,根据请求方法自动找到对应的方法去执行
methods = ["GET", "POST"]
# 对类中的函数都加上装饰器,列表中可以放多个装饰器函数名,依次执行
decorators = []
def get(self):
response_data = {"code": 200, "msg": "我是Login视图的GET方法", "data": None}
return jsonify(response_data)
def post(self):
response_data = {"code": 200, "msg": "我是Login视图的POST方法", "data": None}
return jsonify(response_data)
class Index(views.MethodView):
# 指定请求方法,这里可以省略不写,根据请求方法自动找到对应的方法去执行
methods = ["GET", "POST"]
# 对类中的函数都加上装饰器,列表中可以放多个装饰器函数名,依次执行
decorators = []
def get(self):
v1 = url_for('login')
v2 = url_for('index')
print(v1, v2)
response_data = {"code": 200, "msg": "我是Index视图的GET方法", "data": None}
return jsonify(response_data)
# name其实就是用来填充endpoint的,如果endpoint有值那么name就没有用,且name这个参数是必填的,相当于别名
app.add_url_rule("/login", endpoint=None, view_func=Login.as_view(name="login"))
app.add_url_rule("/index", endpoint=None, view_func=Index.as_view(name="index"))
app.run()
3、自定义路由转换器
(1) FBV
from flask import Flask, views, url_for
from werkzeug.routing import BaseConverter
app = Flask(import_name=__name__)
class RegexConverter(BaseConverter):
"""
自定义URL匹配正则表达式
"""
def __init__(self, map, regex):
super(RegexConverter, self).__init__(map)
self.regex = regex
def to_python(self, value):
"""
路由匹配时,匹配成功后传递给视图函数中参数的值
:param value:
:return:
"""
return int(value)
def to_url(self, value):
"""
使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数
:param value:
:return:
"""
val = super(RegexConverter, self).to_url(value)
return val
# 添加到flask中
app.url_map.converters['regex'] = RegexConverter
@app.route('/index/<regex("\d+"):nid>')
def index(nid):
print(url_for('index', nid='888'))
return 'Index'
if __name__ == '__main__':
app.run()
(2) CBV
from flask import Flask, views, jsonify, url_for
from werkzeug.routing import BaseConverter
app = Flask(__name__)
# 路由转换器
class RegexConverter(BaseConverter):
"""
自定义URL匹配正则表达式
"""
def __init__(self, map, regex):
super(RegexConverter, self).__init__(map)
self.regex = regex
def to_python(self, value):
"""
路由匹配时,匹配成功后传递给视图函数中参数的值
:param value:
:return:
"""
return int(value)
def to_url(self, value):
"""
使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数
:param value:
:return:
"""
val = super(RegexConverter, self).to_url(value)
return val
class Login(views.MethodView):
# 指定请求方法,这里可以省略不写,根据请求方法自动找到对应的方法去执行
methods = ["GET", "POST"]
# 对类中的函数都加上装饰器,列表中可以放多个装饰器函数名,依次执行
decorators = []
def get(self):
response_data = {"code": 200, "msg": "我是Login视图的GET方法", "data": None}
return jsonify(response_data)
def post(self):
response_data = {"code": 200, "msg": "我是Login视图的POST方法", "data": None}
return jsonify(response_data)
class Index(views.MethodView):
# 指定请求方法,这里可以省略不写,根据请求方法自动找到对应的方法去执行
methods = ["GET", "POST"]
# 对类中的函数都加上装饰器,列表中可以放多个装饰器函数名,依次执行
decorators = []
def get(self, nid):
v1 = url_for('login')
v2 = url_for('index', nid=nid)
print(v1, v2)
response_data = {"code": 200, "msg": "我是Index视图的GET方法", "data": None}
return jsonify(response_data)
# 将路由转换器添加到flask中
app.url_map.converters['regex'] = RegexConverter
# name其实就是用来填充endpoint的,如果endpoint有值那么name就没有用,且name这个参数是必填的,相当于别名
app.add_url_rule("/login", endpoint=None, view_func=Login.as_view(name="login"))
app.add_url_rule("/index/<regex('\d+'):nid>", endpoint=None, view_func=Index.as_view(name="index"))
if __name__ == '__main__':
app.run()
4、app.route() 参数
| 参数 | 示例值 | 说明 |
|---|---|---|
| rule | '/' | URL规则 |
| view_func | 'index' | 视图函数名称 |
| defaults | None | 默认值,当URL中无参数时,使用defaults={'k': 'v'}为函数提供参数 |
| endpoint | None | 名称,用于反向生成URL,即: url_for('名称') |
| methods | None | 允许的请求方式,如:["GET","POST"] |
| strict_slashes | None | 对URL最后的 / 符号是否严格要求, |
| redirect_to | None | 重定向到指定地址 |
| subdomain | None | 子域名访问 |
(1) redirect_to 重定向
FBV
from flask import Flask
app = Flask(__name__)
@app.route('/old', methods=['GET', 'POST'], redirect_to='/new')
def old():
return '老功能'
@app.route('/new', methods=['GET', 'POST'])
def new():
return '新功能'
if __name__ == '__main__':
app.run()
CBV
from flask import Flask, views, jsonify, url_for
app = Flask(__name__)
class OldView(views.MethodView):
methods = ["GET", "POST"]
decorators = []
def get(self):
response_data = {"code": 200, "msg": "我是旧方法", "data": None}
return jsonify(response_data)
class NewView(views.MethodView):
methods = ["GET", "POST"]
decorators = []
def get(self):
response_data = {"code": 200, "msg": "我是新方法", "data": None}
return jsonify(response_data)
# name其实就是用来填充endpoint的,如果endpoint有值那么name就没有用,且name这个参数是必填的,相当于别名
app.add_url_rule("/old", endpoint=None, view_func=OldView.as_view(name="login"), redirect_to='/new')
app.add_url_rule("/new", endpoint=None, view_func=NewView.as_view(name="index"))
if __name__ == '__main__':
app.run()
(2) subdomain 子域名访问
修改 host 文件(C:\Windows\System32\drivers\etc)
127.0.0.1 www.xingxing.com
127.0.0.1 api.xingxing.com
FBV
from flask import Flask, views, url_for
app = Flask(__name__)
app.config['SERVER_NAME'] = 'xingxing.com:5000'
@app.route("/", subdomain="api")
def static_index():
return "static.your-domain.tld"
@app.route("/dynamic", subdomain="<username>")
def username_index(username):
return username + ".your-domain.tld"
if __name__ == '__main__':
app.run()
CBV
from flask import Flask, views, url_for
app = Flask(__name__)
app.config['SERVER_NAME'] = 'xingxing.com:5000'
class Login(views.MethodView):
methods = ["GET", "POST"]
decorators = []
def get(self):
response_data = {"code": 200, "msg": "我是Login视图的GET方法", "data": None}
return jsonify(response_data)
class Index(views.MethodView):
methods = ["GET", "POST"]
decorators = []
def get(self, username):
response_data = {"code": 200, "msg": "我是Index视图的GET方法", "data": None}
return jsonify(response_data)
app.add_url_rule("/login", endpoint=None, view_func=Login.as_view(name="login"), subdomain="api")
app.add_url_rule("/index", endpoint=None, view_func=Index.as_view(name="index"), subdomain="<username>")
if __name__ == '__main__':
app.run()
Flask 模型
1、SQLALchemy
(1) 前戏
SQLAlchemy是一个基于Python实现的ORM框架。该框架建立在 DB API之上,使用关系对象映射进行数据库操作,简言之便是:将类和对象转换成SQL,然后使用数据API执行SQL并获取执行结果。
1-1 安装
pip install sqlalchemy
pip install pymysql
1-2 组成部分
- Engine,框架的引擎
- Connection Pooling ,数据库连接池
- Dialect,选择连接数据库的DB API种类
- Schema/Types,架构和类型
- SQL Exprression Language,SQL表达式语言
1-3 链接数据库
SQLAlchemy本身无法操作数据库,其必须以来pymsql等第三方插件,Dialect用于和数据API进行交流,根据配置文件的不同调用不同的数据库API,从而实现对数据库的操作,如:
# 1. MySQL-Python
mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname>
# 2. pymysql
mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>]
# 3. MySQL-Connector
mysql+mysqlconnector://<user>:<password>@<host>[:<port>]/<dbname>
# 4. cx_Oracle
oracle+cx_oracle://user:pass@host:port/dbname[?key=value&key=value...]
# 更多:http://docs.sqlalchemy.org/en/latest/dialects/index.html
(2) 原生 SQL
2-1 基本使用
from sqlalchemy import create_engine
# 创建链接
engine = create_engine("mysql+pymysql://root:19971215@127.0.0.1:3306/flask_test?charset=utf8", max_overflow=5)
# 方式一:
cur = engine.execute("INSERT INTO user (username, password) VALUES ('苍井空', '123')")
print(cur.lastrowid) # 新插入行自增ID
# 方式二:
cur = engine.execute("INSERT INTO user (username, password) VALUES(%s, %s)", [('波多野结衣', '123'), ('小泽玛利亚', '123'), ])
# 方式三:
cur = engine.execute("INSERT INTO user (username, password) VALUES (%(username)s, %(password)s)", username='吉泽明步', password='123')
# 查询
cur = engine.execute('select * from user')
# 获取第一行数据
print(cur.fetchone())
# 获取第n行数据
print(cur.fetchmany(3))
# 获取所有数据
print(cur.fetchall())
2-2 数据库链接池
import time
import threading
import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.engine.base import Engine
# 创建引擎
engine = create_engine(
"mysql+pymysql://root:123@127.0.0.1:3306/t1?charset=utf8",
max_overflow=0, # 超过连接池大小外最多创建的连接
pool_size=5, # 连接池大小
pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置)
)
# 创建任务(查询....)
def task(arg):
conn = engine.raw_connection()
cursor = conn.cursor()
cursor.execute(
"select * from t1"
)
result = cursor.fetchall()
cursor.close()
conn.close()
# 多线程执行
for i in range(20):
t = threading.Thread(target=task, args=(i,))
t.start()
2-3 数据库连接池
import time
import threading
import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.engine.base import Engine
# 创建引擎
engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/t1", max_overflow=0, pool_size=5)
# 创建任务
def task(arg):
conn = engine.contextual_connect()
with conn:
cur = conn.execute(
"select * from t1"
)
result = cur.fetchall()
print(result)
# 多线程执行
for i in range(20):
t = threading.Thread(target=task, args=(i,))
t.start()
2-4 数据库连接池
import time
import threading
import sqlalchemy
from sqlalchemy import create_engine
from sqlalchemy.engine.base import Engine
from sqlalchemy.engine.result import ResultProxy
# 创建引擎
engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/t1", max_overflow=0, pool_size=5)
# 创建任务
def task(arg):
cur = engine.execute("select * from t1")
result = cur.fetchall()
cur.close()
print(result)
# 多线程执行
for i in range(20):
t = threading.Thread(target=task, args=(i,))
t.start()
(3) 基本数据表操作
3-1 创建单表
from datetime import datetime
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, DateTime, Boolean, Date, Enum
Base = declarative_base()
# 创建模型
class UserInfo(Base):
__tablename__ = 'user_info'
Gender = ("男", "女", "其他")
id = Column(Integer, primary_key=True, autoincrement=True, comment="ID")
nickname = Column(String(64), nullable=False, comment="用户昵称")
avatar = Column(String(128), comment="用户头像")
gender = Column(Enum(*Gender), server_default="男", comment="性别")
birthday = Column(Date, default="2000-01-01", comment="生日")
identifier = Column(String(64), nullable=False, unique=True, comment="账号")
credential = Column(String(128), nullable=False, comment="密码凭证")
data_status = Column(Boolean, default=True, comment="数据状态")
create_time = Column(DateTime, default=datetime.now, comment="创建时间")
def init_db():
# 创建链接
engine = create_engine("mysql+pymysql://root:19971215@127.0.0.1:3306/flask_test?charset=utf8", max_overflow=5)
# 创建表
Base.metadata.create_all(engine)
def drop_db():
# 创建链接
engine = create_engine("mysql+pymysql://root:19971215@127.0.0.1:3306/flask_test?charset=utf8", max_overflow=5)
# 删除表
Base.metadata.drop_all(engine)
if __name__ == '__main__':
drop_db()
init_db()
3-2 创建关系表
from datetime import datetime
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy import Column, Integer, String, DateTime, Boolean, ForeignKey, DECIMAL
Base = declarative_base()
# 用户表
class Users(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True, autoincrement=True, comment="ID")
nickname = Column(String(64), nullable=False, comment="用户昵称")
data_status = Column(Boolean, default=True, comment="数据状态")
create_time = Column(DateTime, default=datetime.now, comment="创建时间")
# 用户认证表
class Auths(Base):
__tablename__ = 'auths'
id = Column(Integer, primary_key=True, autoincrement=True, comment="ID")
identifier = Column(String(64), nullable=False, unique=True, comment="账号")
credential = Column(String(128), nullable=False, comment="密码凭证")
data_status = Column(Boolean, default=True, comment="数据状态")
create_time = Column(DateTime, default=datetime.now, comment="创建时间")
user_id = Column(Integer, ForeignKey("users.id"))
# 与生成表结构无关,仅用于查询方便
user = relationship("Users", backref='auths')
# 商品分类
class GoodsCategory(Base):
__tablename__ = 'goods_category'
id = Column(Integer, primary_key=True, autoincrement=True, comment="ID")
title = Column(String(64), nullable=False, comment="商品分类名称")
data_status = Column(Boolean, default=True, comment="数据状态")
create_time = Column(DateTime, default=datetime.now, comment="创建时间")
parent_id = Column(Integer, ForeignKey("goods_category.id"))
# 商品表
class Goods(Base):
__tablename__ = 'goods'
id = Column(Integer, primary_key=True, autoincrement=True, comment="ID")
name = Column(String(128), nullable=False, comment="商品名称")
price = Column(DECIMAL(9, 2), default=0, comment="商品价格")
data_status = Column(Boolean, default=True, comment="数据状态")
create_time = Column(DateTime, default=datetime.now, comment="创建时间")
goods_category_id = Column(Integer, ForeignKey("goods_category.id"))
# 与生成表结构无关,仅用于查询方便
goods_category = relationship("GoodsCategory", backref='goods')
# 购物车
class ShoppingCart(Base):
__tablename__ = 'shopping_cart'
id = Column(Integer, primary_key=True, autoincrement=True, comment="ID")
number = Column(Integer, default=1, comment="数量")
data_status = Column(Boolean, default=True, comment="数据状态")
create_time = Column(DateTime, default=datetime.now, comment="创建时间")
goods_id = Column(Integer, ForeignKey("goods.id"))
user_id = Column(Integer, ForeignKey("users.id"))
def init_db():
"""
根据类创建数据库表
:return:
"""
engine = create_engine(
"mysql+pymysql://root:19971215@127.0.0.1:3306/flask_test?charset=utf8",
max_overflow=0, # 超过连接池大小外最多创建的连接
pool_size=5, # 连接池大小
pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置)
)
Base.metadata.create_all(engine)
def drop_db():
"""
根据类删除数据库表
:return:
"""
engine = create_engine(
"mysql+pymysql://root:19971215@127.0.0.1:3306/flask_test?charset=utf8",
max_overflow=0, # 超过连接池大小外最多创建的连接
pool_size=5, # 连接池大小
pool_timeout=30, # 池中没有线程最多等待的时间,否则报错
pool_recycle=-1 # 多久之后对线程池中的线程进行一次连接的回收(重置)
)
Base.metadata.drop_all(engine)
if __name__ == '__main__':
drop_db()
init_db()
(4) 基本增删改查
4-1 添加数据
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from models import Users
# 创建引擎对象
engine = create_engine("mysql+pymysql://root:19971215@127.0.0.1:3306/flask_test?charset=utf8", max_overflow=0, pool_size=5)
Session = sessionmaker(bind=engine)
session = Session()
# ---------- 方式一: 单个添加 ----------
obj1 = Users(nickname="苍老师")
session.add(obj1)
# ---------- 方式二: 批量添加 ----------
session.add_all([
Users(nickname="波老师"),
Users(nickname="小老师"),
Auths(identifier="admin_001", credential="password_001", user_id=1),
Auths(identifier="admin_002", credential="password_002", user_id=2),
])
# 异常捕获
try:
# 提交事务
session.commit()
except Exception:
# 发生异常回滚
session.rollback()
finally:
# 关闭session
session.close()
4-2 删除数据
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from models import Users
# 创建引擎对象
engine = create_engine("mysql+pymysql://root:19971215@127.0.0.1:3306/flask_test?charset=utf8", max_overflow=0, pool_size=5)
Session = sessionmaker(bind=engine)
session = Session()
# ---------- 方式一: 单个删除 ----------
session.query(Users).filter(Users.id == 1).delete()
# ---------- 方式二: 批量删除 ----------
session.query(Users).filter(Users.id > 3).delete()
# 异常捕获
try:
# 提交事务
session.commit()
except Exception as e:
print(e)
# 发生异常回滚
session.rollback()
finally:
# 关闭session
session.close()
4-3 修改数据
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from models import Users
# 创建引擎对象
engine = create_engine("mysql+pymysql://root:19971215@127.0.0.1:3306/flask_test?charset=utf8", max_overflow=0, pool_size=5)
Session = sessionmaker(bind=engine)
session = Session()
# 修改数据
session.query(Users).filter(Users.id > 0).update({"nickname": "苍老师"})
session.query(Users).filter(Users.id > 0).update({Users.nickname: Users.nickname + "-我爱你"}, synchronize_session=False)
# session.query(Users).filter(Users.id > 0).update({"age": Users.age + 1}, synchronize_session="evaluate")
# 异常捕获
try:
# 提交事务
session.commit()
except Exception as e:
print(e)
# 发生异常回滚
session.rollback()
finally:
# 关闭session
session.close()
4-4 查询数据
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from sqlalchemy.sql import text
from models import Users
# 创建引擎对象
engine = create_engine("mysql+pymysql://root:19971215@127.0.0.1:3306/flask_test?charset=utf8", max_overflow=0, pool_size=5)
Session = sessionmaker(bind=engine)
session = Session()
# 查询出所有用户
r1 = session.query(Users).all()
print(r1)
# 获取指定字段的值
r2 = session.query(Users.nickname.label('xx'), Users.data_status).all()
print(r2)
# 获取用户名为 "苍老师-我爱你" 的所有用户
r3 = session.query(Users).filter(Users.nickname == "苍老师-我爱你").all()
print(r3)
# 获取用户名为 "苍老师-我爱你" 的所有用户
r4 = session.query(Users).filter_by(nickname='苍老师-我爱你').all()
print(r4)
# 获取用户名为 "苍老师-我爱你" 的第一个用户
r5 = session.query(Users).filter_by(nickname='苍老师-我爱你').first()
print(r5)
# 获取用户名为 "苍老师-我爱你" id 小于 100 的所有用户
r6 = session.query(Users).filter(text("id<:value and nickname=:name")).params(value=10, name='苍老师-我爱你').order_by(Users.id).all()
print(r6)
# 获取用户名为 "苍老师-我爱你" 的所有用户
r7 = session.query(Users).from_statement(text("SELECT * FROM users where nickname=:name")).params(name='苍老师-我爱你').all()
print(r7)
(5) 查询常用操作
5-1 条件查询
from sqlalchemy import and_, or_
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from flask_models.models import Users
# 创建引擎对象
engine = create_engine("mysql+pymysql://root:19971215@127.0.0.1:3306/flask_test?charset=utf8", max_overflow=0,
pool_size=5)
Session = sessionmaker(bind=engine)
session = Session()
# 查询用户昵称为 "test_user" 的所有用户
ret = session.query(Users).filter_by(nickname='test_user').all()
print(ret)
# 查询用户id 大于1 并且昵称为 "test_user" 的所有用户
ret = session.query(Users).filter(Users.id > 1, Users.nickname == 'test_user').all()
print(ret)
# 查询用户id在1~3之间 并且昵称为 "test_user" 的所有用户
ret = session.query(Users).filter(Users.id.between(1, 3), Users.nickname == 'test_user').all()
print(ret)
# 查询用户id为[1,3,4]的所有用户
ret = session.query(Users).filter(Users.id.in_([1, 3, 4])).all()
print(ret)
# 查询用户id为 session.query(Users.id).filter_by(nickname='test_user')) 查询出来的结果
ret = session.query(Users).filter(Users.id.in_(session.query(Users.id).filter_by(nickname='test_user'))).all()
print(ret)
# and 查询
ret = session.query(Users).filter(and_(Users.id > 3, Users.nickname == 'test_user')).all()
print(ret)
# or 查询
ret = session.query(Users).filter(or_(Users.id < 2, Users.nickname == 'test_user')).all()
print(ret)
# and 查询 和 or 查询结合
ret = session.query(Users).filter(
or_(
Users.id < 2,
and_(Users.nickname == 'test_user', Users.id > 3),
Users.gender != ""
)).all()
print(ret)
5-2 通配符查询
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from flask_models.models import Users
# 创建引擎对象
engine = create_engine("mysql+pymysql://root:19971215@127.0.0.1:3306/flask_test?charset=utf8", max_overflow=0, pool_size=5)
Session = sessionmaker(bind=engine)
session = Session()
# 模糊查询用户昵称以 admin 开头的所有用户
ret = session.query(Users.nickname).filter(Users.nickname.like('admin%')).all()
print(ret)
# 模糊查询用户昵称以 user 结尾的所有用户
ret = session.query(Users.nickname).filter(Users.nickname.like('%user')).all()
print(ret)
# 模糊查询用户昵称包含 _use 的所有用户
ret = session.query(Users.nickname).filter(Users.nickname.like('%_use%')).all()
print(ret)
# 模糊查询用户昵称不以 admin 开头的所有用户
ret = session.query(Users.nickname).filter(~Users.nickname.like('admin%')).all()
print(ret)
5-3 限制查询
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from flask_models.models import Users
# 创建引擎对象
engine = create_engine("mysql+pymysql://root:19971215@127.0.0.1:3306/flask_test?charset=utf8", max_overflow=0, pool_size=5)
Session = sessionmaker(bind=engine)
session = Session()
# 限制查询
ret = session.query(Users)[1:2]
print(ret)
# 限制查询3条
ret = session.query(Users).limit(3).all()
print(ret, '=================')
# 从第六个开始获取所有
ret = session.query(Users).offset(5).all()
print(ret)
# 从第三个开始获取三条
ret = session.query(Users).offset(2).limit(3).all()
print(ret)
# 获取第三个到第五个数据
ret = session.query(Users.id).slice(2, 5).all()
print(ret)
5-4 排序查询
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from flask_models.models import Users
# 创建引擎对象
engine = create_engine("mysql+pymysql://root:19971215@127.0.0.1:3306/flask_test?charset=utf8", max_overflow=0, pool_size=5)
Session = sessionmaker(bind=engine)
session = Session()
# 查询所有用户 并根据年龄倒序排列
ret = session.query(Users).order_by(Users.age.desc()).all()
print(ret)
# 查询所有用户 并根据用户年龄倒序排列 如果年龄一样则按照正序排列
ret = session.query(Users).order_by(Users.age.desc(), Users.id.asc()).all()
print(ret)
5-5 分组查询
from sqlalchemy.sql import func
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from flask_models.models import Users
# 创建引擎对象
engine = create_engine("mysql+pymysql://root:19971215@127.0.0.1:3306/flask_test?charset=utf8", max_overflow=0, pool_size=5)
Session = sessionmaker(bind=engine)
session = Session()
# 根据用户年龄分组
# ret = session.query(Users).group_by(Users.nickname).all()
# print(ret)
ret = session.query(func.max(Users.id), func.sum(Users.id), func.min(Users.id)).group_by(Users.nickname).all()
print(ret)
ret = session.query(func.max(Users.id), func.sum(Users.id), func.min(Users.id)).group_by(Users.nickname).having(func.min(Users.id) > 2).all()
print(ret)
5-6 连表查询
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from flask_models.models import Users, Auths, GoodsCategory, Goods
# 创建引擎对象
engine = create_engine("mysql+pymysql://root:19971215@127.0.0.1:3306/flask_test?charset=utf8", max_overflow=0, pool_size=5)
Session = sessionmaker(bind=engine)
session = Session()
# 连表
ret = session.query(Users, Auths).filter(Users.id == Auths.user_id).all()
print(ret)
ret = session.query(GoodsCategory).join(Goods).all()
print(ret)
ret = session.query(GoodsCategory).join(Goods, isouter=True).all()
print(ret)
5-7 组合查询
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from flask_models.models import Users, Auths, GoodsCategory, Goods
# 创建引擎对象
engine = create_engine("mysql+pymysql://root:19971215@127.0.0.1:3306/flask_test?charset=utf8", max_overflow=0, pool_size=5)
Session = sessionmaker(bind=engine)
session = Session()
# 组合
q1 = session.query(Users.nickname).filter(Users.id > 2)
q2 = session.query(Auths.identifier).filter(Auths.user_id < 2)
ret = q1.union(q2).all()
print(ret)
q1 = session.query(Users.nickname).filter(Users.id > 2)
q2 = session.query(Auths.identifier).filter(Auths.user_id < 2)
ret = q1.union_all(q2).all()
print(ret)
(6) 一对多操作
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
from flask_models.models import Users, Auths
# 创建引擎对象
engine = create_engine("mysql+pymysql://root:19971215@127.0.0.1:3306/flask_test?charset=utf8", max_overflow=0, pool_size=5)
Session = sessionmaker(bind=engine)
session = Session()
# 使用relationship正向查询
v = session.query(Auths).first()
print(v.id)
print(v.user.nickname)
# 使用relationship反向查询
v = session.query(Users).first()
print(v.nickname)
print(v.auths)
(7) 多对多操作
from flask_demo.models import Users, Goods, ShoppingCart, GoodsCategory
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
# 创建链接
engine = create_engine("mysql+pymysql://root:19971215@127.0.0.1:3306/flask_test?charset=utf8", max_overflow=5)
Session = sessionmaker(bind=engine)
session = Session()
"""
# 添加数据
session.add(Users(nickname="test_user_1"))
session.commit()
session.add(GoodsCategory(title="数码"))
session.commit()
session.add(Goods(name="小米", price=666, goods_category_id=1))
session.commit()
session.add(ShoppingCart(number=1, goods_id=1, users_id=1))
session.commit()
# 同时操作三张表 - 正向
obj = Users(nickname="xiaoming")
obj.goods_data = [Goods(name="联想", price=888, goods_category_id=1), Goods(name="OPPO", price=333, goods_category_id=1)]
session.add(obj)
session.commit()
# 同时操作三张表 - 反向
obj = Goods(name="华为", price=999, goods_category_id=1)
obj.users_data = [Users(nickname="test_user_4"), Users(nickname="test_user_3")]
session.add(obj)
session.commit()
"""
# 使用relationship正向查询
v = session.query(Users).first()
print(v.nickname)
print(v.goods_data)
# 使用relationship反向查询
v = session.query(Goods).first()
print(v.name, v.price)
print(v.users_data)
(8) 其他
import time
import threading
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, Index
from sqlalchemy.orm import sessionmaker, relationship
from sqlalchemy import create_engine
from sqlalchemy.sql import text, func
from sqlalchemy.engine.result import ResultProxy
from db import Users, Hosts, Hobby, Person, Group, Server, Server2Group
engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/s6?charset=utf8", max_overflow=0, pool_size=5)
Session = sessionmaker(bind=engine)
session = Session()
# 关联子查询
subqry = session.query(func.count(Server.id).label("sid")).filter(Server.id == Group.id).correlate(Group).as_scalar()
result = session.query(Group.name, subqry)
"""
SELECT `group`.name AS group_name, (SELECT count(server.id) AS sid
FROM server
WHERE server.id = `group`.id) AS anon_1
FROM `group`
"""
# 原生SQL
"""
# 查询
cursor = session.execute('select * from users')
result = cursor.fetchall()
# 添加
cursor = session.execute('insert into users(name) values(:value)',params={"value":'wupeiqi'})
session.commit()
print(cursor.lastrowid)
"""
session.close()
2、flask-script
安装
pip install flask-script
(1) 基本使用
from flask_script_demo import create_app
from flask_script import Manager
app = create_app()
manage = Manager(app)
@manage.command
def custom(arg):
"""
自定义命令: python manage.py customc 123
"""
print(arg)
@manage.option('-n', '--name', dest='name')
@manage.option('-u', '--url', dest='url')
def cmd(name, url):
"""
自定义命令:
python manage.py cmd -n flask-script -u http://www.baidu.com
python manage.py cmd --name flask-script --url http://www.baidu.com
"""
print(name, url)
if __name__ == '__main__':
# app.run()
manage.run() # python manage.py runserver
3、flask-sqlalchemy
(1) 前戏
项目目录

安装
pip install flask-sqlalchemy
作用
-
将 SQLAlchemy 相关的所有功能都封装到db = flask_sqlalchemy.SQLAlchemy() 对象中
-
创建表
-
操作表
(2) 创建表
from datetime import datetime
from flask_script_demo import db
# 用户表
class Users(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True, autoincrement=True, comment="ID")
nickname = db.Column(db.String(64), nullable=False, comment="用户昵称")
data_status = db.Column(db.Boolean, default=True, comment="数据状态")
create_time = db.Column(db.DateTime, default=datetime.now, comment="创建时间")
(3) 离线脚本
"""
Web 运行时, flask程序运行起来,用户通过浏览器访问
离线脚本, 自定义一个py 文件 + 使用flask中定义好的功能
"""
from flask_script_demo import db
from flask_script_demo import create_app
from flask_script_demo.users import models
app = create_app()
with app.app_context():
# 删除表
# db.drop_all()
# 创建表
# db.create_all()
# 查询
data = db.session.query(models.Users).all()
print(data)
(4) 案例
settings.py
# 基础配置
class BaseConfig(object):
DEBUG = False
TESTING = False
# SQLALCHEMY 相关配置
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:19971215@127.0.0.1:3306/flask_test?charset=utf8'
SQLALCHEMY_TRACK_MODIFICATIONS = True
SQLALCHEMY_POOL_SIZE = 5
SQLALCHEMY_POOL_RECYCLE = -1
SQLALCHEMY_POOL_TIMEOUT = 30
# 开发环境
class DevelopmentConfig(BaseConfig):
DATABASES_URL = "mysql://root@dev/foo"
DEBUG = True
manage.py
from flask_script_demo import create_app
from flask_script import Manager
app = create_app()
manage = Manager(app)
# 自定制命令
@manage.command
def custom(arg):
"""
自定义命令: python manage.py customc 123
"""
print(arg)
# 自定制命令
@manage.option('-n', '--name', dest='name')
@manage.option('-u', '--url', dest='url')
def cmd(name, url):
"""
自定义命令:
python manage.py cmd -n flask-script -u http://www.baidu.com
python manage.py cmd --name flask-script --url http://www.baidu.com
"""
print(name, url)
if __name__ == '__main__':
# app.run()
manage.run() # python manage.py runserver
views.py
from flask import views, request, jsonify
from flask_script_demo import db
from flask_script_demo.users import models
# 个人中心接口
class UserInfoView(views.MethodView):
methods = ["GET"]
decorators = []
def get(self):
response_data = {'code': 200, 'msg': '个人信息', 'data': None}
obj = db.session.query(models.Users).all()
print(obj)
return jsonify(response_data)
flask_script_demo/_init_.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
# db 就包含了 app 和 SQLAlchemy 的所有操作
db = SQLAlchemy()
# 创建app
def create_app():
app = Flask(__name__)
# app.config["DEBUG"] = True
app.config.from_object('settings.DevelopmentConfig')
# 注册蓝图
from flask_script_demo.users import users
from flask_script_demo.account import account
# base_path = "api/v1"
# app.register_blueprint(users, url_prefix=f"/{base_path}/users")
# app.register_blueprint(account, url_prefix=f"/{base_path}/account")
app.register_blueprint(users)
app.register_blueprint(account)
# 初始化 db
db.init_app(app)
return app
flask_script_demo/users/models.py
from datetime import datetime
from flask_script_demo import db
# 用户表
class Users(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True, autoincrement=True, comment="ID")
nickname = db.Column(db.String(64), nullable=False, comment="用户昵称")
data_status = db.Column(db.Boolean, default=True, comment="数据状态")
create_time = db.Column(db.DateTime, default=datetime.now, comment="创建时间")
flask_script_demo/users/urls.py
from . import users
from . import views
users.add_url_rule("/login", endpoint=None, view_func=views.LoginView.as_view(name="login"))
users.add_url_rule("/user_info", endpoint=None, view_func=views.UserInfoView.as_view(name="user_info"))
flask_script_demo/users/views.py
from flask import views, request, jsonify
from flask_script_demo import db
from flask_script_demo.users import models
# 登录接口
class LoginView(views.MethodView):
methods = ["POST"]
decorators = []
def post(self):
response_data = {'code': 200, 'msg': '登录成功', 'data': None}
print(request.get_json())
return jsonify(response_data)
# 个人中心接口
class UserInfoView(views.MethodView):
methods = ["GET"]
decorators = []
def get(self):
response_data = {'code': 200, 'msg': '个人信息', 'data': None}
obj = db.session.query(models.Users).all()
print(obj)
print(request.get_json())
return jsonify(response_data)
4、flask-migrate
(1) 前戏
作用
- 做数据库迁移
依赖
- flask-script
- flask-sqlalchemy
安装
- pip install flask-migrate
(2) 基本使用
from flask_script_demo import create_app
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
from flask_script_demo import db
app = create_app()
manage = Manager(app)
"""
数据库迁移命令:
python manage.py db init
python manage.py db migrate
python manage.py db upgrade
"""
Migrate(app, db)
manage.add_command('db', MigrateCommand)
if __name__ == '__main__':
# app.run()
manage.run() # python manage.py runserver
5、自定义组件
Flask 请求
常用方法
| 方法 | 示例 | 说明 |
|---|---|---|
| request.path | /request | 获取请求路径 |
| request.method | GET | 获取请求方式 |
| request.headers | Host: 127.0.0.1:5000 Connection: keep-alive ... |
获取请求头 |
| request.user_agent | Mozilla/5.0 (Windows NT 10.0; WOW64) App... | 获取UserAgent |
| request.host | 127.0.0.1:5000 | 获取请求主机 |
| request.url | http://127.0.0.1:5000/request?name=xxx&age=22 | 获取请求完整链接 |
| request.host_url | http://127.0.0.1:5000/ | 请求主机URL |
| request.base_url | http://127.0.0.1:5000/request | 请求地址 |
| request.full_path | /request?name=xxx&age=22 | 查询字符串 |
| request.url_charset | utf-8 | 请求字符集 |
| request.url_root | http://127.0.0.1:5000/ | 请求根路由 |
| request.url_rule | /request | 请求路径 |
| request.args | ImmutableMultiDict([('name', 'xxx'), ('age', '22')]) | 请求数据 |
| request.data | b'' | 请求数据 |
| request.get_data | 请求数据 | |
| request.get_json | 请求数据 | |
| request.files | 请求数据 | |
| request.form | 请求数据 | |
| request.values | 请求数据 | |
| request.view_args | 请求数据 | |
| request.cookies | 请求Cookies | |
| request.authorization | 获取身份认证凭证 |
python查看一个对象可用的方法:
print(dir(request))
1、GET 请求
(1) FBV
from flask import Flask, views, jsonify, request
app = Flask(__name__)
app.config['DEBUG'] = True
@app.route('/request', methods=['GET'])
def request_view():
response_data = {"code": 200, "msg": "success", "data": None}
# 获取请求参数
# ---------- request.args ----------
print(request.args)
name = request.args.get("name", str)
age = request.args.get("age", int)
# ---------- request.values ----------
print(request.values)
print(request.values.get("name", str))
print(request.values.get("age", int))
response_data["msg"] = f"大家好我是{name}我今年{age}岁了"
return jsonify(response_data)
if __name__ == '__main__':
app.run()
(2) CBV
from flask import Flask, views, jsonify, request
app = Flask(__name__)
app.config['DEBUG'] = True
# 视图函数
class RequestView(views.MethodView):
methods = ["GET"]
decorators = []
def get(self):
response_data = {"code": 200, "msg": "success", "data": None}
# 获取请求参数
# ---------- request.args ----------
print(request.args)
name = request.args.get("name", str)
age = request.args.get("age", int)
# ---------- request.values ----------
print(request.values)
print(request.values.get("name", str))
print(request.values.get("age", int))
response_data["msg"] = f"大家好我是{name}我今年{age}岁了"
return jsonify(response_data)
# 注册路由
app.add_url_rule("/request", endpoint=None, view_func=RequestView.as_view(name="request"))
if __name__ == '__main__':
app.run()
2、POST 请求
(1) FBV
form 传参
Content-Type: multipart/form-data;
from flask import Flask, views, jsonify, request
app = Flask(__name__)
app.config['DEBUG'] = True
@app.route('/request', methods=['POST'])
def request_view():
response_data = {"code": 200, "msg": "success", "data": None}
# 获取请求参数
# ---------- request.form ----------
print(request.form)
name = request.form.get("name", default=None, type=str)
age = request.form.get("age", default=None, type=int)
# ---------- request.values ----------
print(request.values)
print(request.values.get("name", default=None, type=str))
print(request.values.get("age", default=None, type=int))
response_data["msg"] = f"大家好我是{name}我今年{age}岁了"
return jsonify(response_data)
if __name__ == '__main__':
app.run()
Json 传参
Content-Type: application/json;
import json
from flask import Flask, views, jsonify, request
app = Flask(__name__)
app.config['DEBUG'] = True
@app.route('/request', methods=['POST'])
def request_view():
response_data = {"code": 200, "msg": "success", "data": None}
# 获取请求参数
# ---------- request.data ----------
print(request.data, type(request.data))
request_data_1 = json.loads(request.data)
print(request_data_1, type(request_data_1))
# ---------- request.get_data() ----------
print(request.get_data(), type(request.get_data()))
request_data_2 = json.loads(request.get_data())
print(request_data_2, type(request_data_2))
# ---------- request.get_json() ----------
print(request.get_json(silent=True), type(request.get_json()))
request_data = request.get_json()
name = request_data.get('name', None)
age = request_data.get('age', None)
response_data["msg"] = f"大家好我是{name}我今年{age}岁了"
return jsonify(response_data)
if __name__ == '__main__':
app.run()
(2) CBV
form 传参
Content-Type: multipart/form-data;
from flask import Flask, views, jsonify, request
app = Flask(__name__)
app.config['DEBUG'] = True
class RequestView(views.MethodView):
methods = ["POST"]
decorators = []
def post(self):
response_data = {"code": 200, "msg": "success", "data": None}
# 获取请求参数
# ---------- request.form ----------
print(request.form)
name = request.form.get("name", default=None, type=str)
age = request.form.get("age", default=None, type=int)
# ---------- request.values ----------
print(request.values)
print(request.values.get("name", default=None, type=str))
print(request.values.get("age", default=None, type=int))
response_data["msg"] = f"大家好我是{name}我今年{age}岁了"
return jsonify(response_data)
# 注册路由
app.add_url_rule("/request", endpoint=None, view_func=RequestView.as_view(name="request"))
if __name__ == '__main__':
app.run()
Json 传参
Content-Type: application/json;
import json
from flask import Flask, views, jsonify, request
app = Flask(__name__)
app.config['DEBUG'] = True
class RequestView(views.MethodView):
methods = ["POST"]
decorators = []
def post(self):
response_data = {"code": 200, "msg": "success", "data": None}
# 获取请求参数
# ---------- request.data ----------
print(request.data, type(request.data))
request_data_1 = json.loads(request.data)
print(request_data_1, type(request_data_1))
# ---------- request.get_data() ----------
print(request.get_data(), type(request.get_data()))
request_data_2 = json.loads(request.get_data())
print(request_data_2, type(request_data_2))
# ---------- request.get_json() ----------
print(request.get_json(silent=True), type(request.get_json()))
request_data = request.get_json()
name = request_data.get('name', None)
age = request_data.get('age', None)
response_data["msg"] = f"大家好我是{name}我今年{age}岁了"
return jsonify(response_data)
# 注册路由
app.add_url_rule("/request", endpoint=None, view_func=RequestView.as_view(name="request"))
if __name__ == '__main__':
app.run()
3、PUT 请求
(1) FBV
form 传参
Content-Type: multipart/form-data;
from flask import Flask, views, jsonify, request
app = Flask(__name__)
app.config['DEBUG'] = True
@app.route('/request', methods=['PUT'])
def request_view():
response_data = {"code": 200, "msg": "success", "data": None}
# 获取请求参数
# ---------- request.form ----------
print(request.form)
name = request.form.get("name", default=None, type=str)
age = request.form.get("age", default=None, type=int)
# ---------- request.values ----------
print(request.values)
print(request.values.get("name", default=None, type=str))
print(request.values.get("age", default=None, type=int))
response_data["msg"] = f"大家好我是{name}我今年{age}岁了"
return jsonify(response_data)
if __name__ == '__main__':
app.run()
Json 传参
Content-Type: application/json;
import json
from flask import Flask, views, jsonify, request
app = Flask(__name__)
app.config['DEBUG'] = True
@app.route('/request', methods=['PUT'])
def request_view():
response_data = {"code": 200, "msg": "success", "data": None}
# 获取请求参数
# ---------- request.data ----------
print(request.data, type(request.data))
request_data_1 = json.loads(request.data)
print(request_data_1, type(request_data_1))
# ---------- request.get_data() ----------
print(request.get_data(), type(request.get_data()))
request_data_2 = json.loads(request.get_data())
print(request_data_2, type(request_data_2))
# ---------- request.get_json() ----------
print(request.get_json(silent=True), type(request.get_json()))
request_data = request.get_json()
name = request_data.get('name', None)
age = request_data.get('age', None)
response_data["msg"] = f"大家好我是{name}我今年{age}岁了"
return jsonify(response_data)
if __name__ == '__main__':
app.run()
(2) CBV
form 传参
Content-Type: multipart/form-data;
from flask import Flask, views, jsonify, request
app = Flask(__name__)
app.config['DEBUG'] = True
class RequestView(views.MethodView):
methods = ["PUT"]
decorators = []
def put(self):
response_data = {"code": 200, "msg": "success", "data": None}
# 获取请求参数
# ---------- request.form ----------
print(request.form)
name = request.form.get("name", default=None, type=str)
age = request.form.get("age", default=None, type=int)
# ---------- request.values ----------
print(request.values)
print(request.values.get("name", default=None, type=str))
print(request.values.get("age", default=None, type=int))
response_data["msg"] = f"大家好我是{name}我今年{age}岁了"
return jsonify(response_data)
# 注册路由
app.add_url_rule("/request", endpoint=None, view_func=RequestView.as_view(name="request"))
if __name__ == '__main__':
app.run()
Json 传参
Content-Type: application/json;
import json
from flask import Flask, views, jsonify, request
app = Flask(__name__)
app.config['DEBUG'] = True
class RequestView(views.MethodView):
methods = ["PUT"]
decorators = []
def put(self):
response_data = {"code": 200, "msg": "success", "data": None}
# 获取请求参数
# ---------- request.data ----------
print(request.data, type(request.data))
request_data_1 = json.loads(request.data)
print(request_data_1, type(request_data_1))
# ---------- request.get_data() ----------
print(request.get_data(), type(request.get_data()))
request_data_2 = json.loads(request.get_data())
print(request_data_2, type(request_data_2))
# ---------- request.get_json() ----------
print(request.get_json(silent=True), type(request.get_json()))
request_data = request.get_json()
name = request_data.get('name', None)
age = request_data.get('age', None)
response_data["msg"] = f"大家好我是{name}我今年{age}岁了"
return jsonify(response_data)
# 注册路由
app.add_url_rule("/request", endpoint=None, view_func=RequestView.as_view(name="request"))
if __name__ == '__main__':
app.run()
4、PATCH 请求
(1) FBV
form 传参
Content-Type: multipart/form-data;
from flask import Flask, views, jsonify, request
app = Flask(__name__)
app.config['DEBUG'] = True
@app.route('/request', methods=['PATCH'])
def request_view():
response_data = {"code": 200, "msg": "success", "data": None}
# 获取请求参数
# ---------- request.form ----------
print(request.form)
name = request.form.get("name", default=None, type=str)
age = request.form.get("age", default=None, type=int)
# ---------- request.values ----------
print(request.values)
print(request.values.get("name", default=None, type=str))
print(request.values.get("age", default=None, type=int))
response_data["msg"] = f"大家好我是{name}我今年{age}岁了"
return jsonify(response_data)
if __name__ == '__main__':
app.run()
Json 传参
Content-Type: application/json;
import json
from flask import Flask, views, jsonify, request
app = Flask(__name__)
app.config['DEBUG'] = True
@app.route('/request', methods=['PATCH'])
def request_view():
response_data = {"code": 200, "msg": "success", "data": None}
# 获取请求参数
# ---------- request.data ----------
print(request.data, type(request.data))
request_data_1 = json.loads(request.data)
print(request_data_1, type(request_data_1))
# ---------- request.get_data() ----------
print(request.get_data(), type(request.get_data()))
request_data_2 = json.loads(request.get_data())
print(request_data_2, type(request_data_2))
# ---------- request.get_json() ----------
print(request.get_json(silent=True), type(request.get_json()))
request_data = request.get_json()
name = request_data.get('name', None)
age = request_data.get('age', None)
response_data["msg"] = f"大家好我是{name}我今年{age}岁了"
return jsonify(response_data)
if __name__ == '__main__':
app.run()
(2) CBV
form 传参
Content-Type: multipart/form-data;
from flask import Flask, views, jsonify, request
app = Flask(__name__)
app.config['DEBUG'] = True
class RequestView(views.MethodView):
methods = ["PATCH"]
decorators = []
def patch(self):
response_data = {"code": 200, "msg": "success", "data": None}
# 获取请求参数
# ---------- request.form ----------
print(request.form)
name = request.form.get("name", default=None, type=str)
age = request.form.get("age", default=None, type=int)
# ---------- request.values ----------
print(request.values)
print(request.values.get("name", default=None, type=str))
print(request.values.get("age", default=None, type=int))
response_data["msg"] = f"大家好我是{name}我今年{age}岁了"
return jsonify(response_data)
# 注册路由
app.add_url_rule("/request", endpoint=None, view_func=RequestView.as_view(name="request"))
if __name__ == '__main__':
app.run()
Json 传参
Content-Type: application/json;
import json
from flask import Flask, views, jsonify, request
app = Flask(__name__)
app.config['DEBUG'] = True
class RequestView(views.MethodView):
methods = ["PATCH"]
decorators = []
def patch(self):
response_data = {"code": 200, "msg": "success", "data": None}
# 获取请求参数
# ---------- request.data ----------
print(request.data, type(request.data))
request_data_1 = json.loads(request.data)
print(request_data_1, type(request_data_1))
# ---------- request.get_data() ----------
print(request.get_data(), type(request.get_data()))
request_data_2 = json.loads(request.get_data())
print(request_data_2, type(request_data_2))
# ---------- request.get_json() ----------
print(request.get_json(silent=True), type(request.get_json()))
request_data = request.get_json()
name = request_data.get('name', None)
age = request_data.get('age', None)
response_data["msg"] = f"大家好我是{name}我今年{age}岁了"
return jsonify(response_data)
# 注册路由
app.add_url_rule("/request", endpoint=None, view_func=RequestView.as_view(name="request"))
if __name__ == '__main__':
app.run()
5、DELETE 请求
(1) FBV
form 传参
Content-Type: multipart/form-data;
from flask import Flask, views, jsonify, request
app = Flask(__name__)
app.config['DEBUG'] = True
@app.route('/request/<int:id>', methods=['DELETE'])
def request_view():
response_data = {"code": 200, "msg": "success", "data": None}
# 获取请求参数
response_data["msg"] = "删除成功"
return jsonify(response_data)
if __name__ == '__main__':
app.run()
(2) CBV
import json
from flask import Flask, views, jsonify, request
app = Flask(__name__)
app.config['DEBUG'] = True
class RequestView(views.MethodView):
methods = ["DELETE"]
decorators = []
def delete(self):
response_data = {"code": 200, "msg": "success", "data": None}
# 获取请求参数
response_data["msg"] = "删除成功"
return jsonify(response_data)
# 注册路由
app.add_url_rule("/request/<int:id>", endpoint=None, view_func=RequestView.as_view(name="request"))
if __name__ == '__main__':
app.run()
Flask 响应
1、响应字符串
import json
from flask import Flask, views
app = Flask(__name__)
app.config['DEBUG'] = True
class ResponseView(views.MethodView):
methods = ["GET", "POST", "PUT", "PATCH", "DELETE"]
decorators = []
def get(self):
return "我是 ResponseView 的 GET 方法"
def post(self):
return "我是 ResponseView 的 POST 方法"
def put(self):
return "我是 ResponseView 的 PUT 方法"
def patch(self):
return "我是 ResponseView 的 PATCH 方法"
def delete(self):
return "我是 ResponseView 的 DELETE 方法"
# 注册路由
app.add_url_rule("/response", endpoint=None, view_func=ResponseView.as_view(name="response"))
if __name__ == '__main__':
app.run()
2、响应 Response 对象
from flask import Flask, views, jsonify, request, render_template, make_response
app = Flask(__name__)
app.config['DEBUG'] = True
class ResponseView(views.MethodView):
methods = ["GET", "POST", "PUT", "PATCH", "DELETE"]
decorators = []
def get(self):
response = make_response(render_template('index.html'))
response.delete_cookie('key')
response.set_cookie('key', 'value')
response.headers['X-Something'] = 'A value'
return response
def post(self):
response = make_response("我是 ResponseView 的 POST 方法")
response.delete_cookie('key')
response.set_cookie('key', 'value')
response.headers['X-Something'] = 'A value'
return response
def put(self):
response = make_response("我是 ResponseView 的 PUT 方法")
response.delete_cookie('key')
response.set_cookie('key', 'value')
response.headers['X-Something'] = 'A value'
return response
def patch(self):
response = make_response("我是 ResponseView 的 PATCH 方法")
response.delete_cookie('key')
response.set_cookie('key', 'value')
response.headers['X-Something'] = 'A value'
return response
def delete(self):
response = make_response("我是 ResponseView 的 DELETE 方法")
response.delete_cookie('key')
response.set_cookie('key', 'value')
response.headers['X-Something'] = 'A value'
return response
# 注册路由
app.add_url_rule("/response", endpoint=None, view_func=ResponseView.as_view(name="response"))
if __name__ == '__main__':
app.run()
3、返回重定向类型
from flask import Flask, views, redirect
app = Flask(__name__)
app.config['DEBUG'] = True
# 首页视图函数
class IndexView(views.MethodView):
methods = ["GET"]
decorators = []
def get(self):
return "我是首页页面"
# 响应视图函数
class ResponseView(views.MethodView):
methods = ["GET", "POST", "PUT", "PATCH", "DELETE"]
decorators = []
def get(self):
return redirect('/index')
def post(self):
return redirect('/index')
def put(self):
return redirect('/index')
def patch(self):
return redirect('/index')
def delete(self):
return redirect('/index')
# 注册路由
app.add_url_rule("/index", endpoint=None, view_func=IndexView.as_view(name="index"))
app.add_url_rule("/response", endpoint=None, view_func=ResponseView.as_view(name="response"))
if __name__ == '__main__':
app.run()
4、返回处理错误码
from flask import Flask, views, abort
app = Flask(__name__)
app.config['DEBUG'] = True
class ResponseView(views.MethodView):
methods = ["GET", "POST", "PUT", "PATCH", "DELETE"]
decorators = []
def get(self):
return abort(404)
def post(self):
return abort(400)
def put(self):
return abort(401)
def patch(self):
return abort(403)
def delete(self):
return abort(500)
# 注册路由
app.add_url_rule("/response", endpoint=None, view_func=ResponseView.as_view(name="response"))
if __name__ == '__main__':
app.run()
5、返回模板
from flask import Flask, views, render_template
app = Flask(__name__)
app.config['DEBUG'] = True
class ResponseView(views.MethodView):
methods = ["GET", "POST", "PUT", "PATCH", "DELETE"]
decorators = []
def get(self):
return render_template('index.html')
def post(self):
return render_template('index.html')
def put(self):
return render_template('index.html')
def patch(self):
return render_template('index.html')
def delete(self):
return render_template('index.html')
# 注册路由
app.add_url_rule("/response", endpoint=None, view_func=ResponseView.as_view(name="response"))
if __name__ == '__main__':
app.run()
6、返回 json 数据
from flask import Flask, views, jsonify
app = Flask(__name__)
app.config['DEBUG'] = True
class ResponseView(views.MethodView):
methods = ["GET", "POST", "PUT", "PATCH", "DELETE"]
decorators = []
def get(self):
response_data = {"code": 200, "msg": "我是 ResponseView 的 GET 方法", "data": None}
return jsonify(response_data)
def post(self):
response_data = {"code": 200, "msg": "我是 ResponseView 的 POST 方法", "data": None}
return jsonify(response_data)
def put(self):
response_data = {"code": 200, "msg": "我是 ResponseView 的 PUT 方法", "data": None}
return jsonify(response_data)
def patch(self):
response_data = {"code": 200, "msg": "我是 ResponseView 的 PATCH 方法", "data": None}
return jsonify(response_data)
def delete(self):
response_data = {"code": 200, "msg": "我是 ResponseView 的 DELETE 方法", "data": None}
return jsonify(response_data)
# 注册路由
app.add_url_rule("/response", endpoint=None, view_func=ResponseView.as_view(name="response"))
if __name__ == '__main__':
app.run()
Flask 视图
http://www.bjhee.com/flask-ad4.html
1、视图装饰器
(1) FBV
from functools import wraps
from flask import Flask, request, jsonify, session, abort, Response
from random import choice
app = Flask(__name__)
app.config['DEBUG'] = True
# 在启用Session的时候,一定要有它
SECRET_KEY = '-67=d2cg8@d0zw+c268sj84s61yc$b_e2*e^(i9d-^bb+hbqom'
app.secret_key = SECRET_KEY
app.config['PERMANENT_SESSION_LIFETIME'] = 60
# 定义认证装饰器
def login_required(func):
@wraps(func)
def decorated_function(*args, **kwargs):
if not 'username' in session:
abort(401)
return func(*args, **kwargs)
return decorated_function
# 登录接口
@app.route('/login', methods=["POST"])
def login():
response_data = {"code": 200, "msg": "success", "data": None}
request_data = request.get_json()
username = request_data.get('username', None)
password = request_data.get('password', None)
if not all([username, password]):
response_data["msg"] = "必填项不能为空"
if username == "admin" and password == "1234":
session['username'] = username
return jsonify(response_data)
# 退出登录接口
@app.route('/logout', methods=["GET"])
@login_required
def logout():
response_data = {"code": 200, "msg": "success", "data": None}
session.pop('username', None)
return jsonify(response_data)
# 个人中心接口
@app.route('/user_info', methods=['GET'])
@login_required
def user_info():
name = session.get("username")
print(name)
response_data = {"code": 200, "msg": "success", "data": None}
return jsonify(response_data)
if __name__ == '__main__':
app.run()
(2) CBV
from functools import wraps
from flask import Flask, request, views, jsonify, session, abort
from random import choice
app = Flask(__name__)
app.config['DEBUG'] = True
# 在启用Session的时候,一定要有它
SECRET_KEY = '-67=d2cg8@d0zw+c268sj84s61yc$b_e2*e^(i9d-^bb+hbqom'
app.secret_key = SECRET_KEY
app.config['PERMANENT_SESSION_LIFETIME'] = 60
# 定义认证装饰器
def login_required(func):
@wraps(func)
def decorated_function(*args, **kwargs):
if not 'username' in session:
abort(401)
return func(*args, **kwargs)
return decorated_function
# 登录接口
class LoginView(views.MethodView):
methods = ["POST"]
decorators = []
def post(self):
response_data = {"code": 200, "msg": "success", "data": None}
request_data = request.get_json()
username = request_data.get('username', None)
password = request_data.get('password', None)
if not all([username, password]):
response_data["msg"] = "必填项不能为空"
if username == "admin" and password == "1234":
session['username'] = username
return jsonify(response_data)
# 退出登录接口
class LogoutView(views.MethodView):
methods = ["GET"]
decorators = [login_required, ]
def get(self):
response_data = {"code": 200, "msg": "success", "data": None}
session.pop('username', None)
return jsonify(response_data)
# 个人中心接口
class UserInfoView(views.MethodView):
methods = ["GET"]
decorators = []
def get(self):
name = session.get("username")
print(name)
response_data = {"code": 200, "msg": "success", "data": None}
return jsonify(response_data)
app.add_url_rule("/login", endpoint=None, view_func=LoginView.as_view(name="login"))
app.add_url_rule("/logout", endpoint=None, view_func=LogoutView.as_view(name="logout"))
app.add_url_rule("/user_info", endpoint=None, view_func=UserInfoView.as_view(name="user_info"))
if __name__ == '__main__':
app.run()
2、URL集中映射
(1) FBV
from functools import wraps
from flask import Flask, jsonify, session, abort, views
app = Flask(__name__)
app.config["DEBUG"] = True
# 在启用Session的时候,一定要有它
SECRET_KEY = '-67=d2cg8@d0zw+c268sj84s61yc$b_e2*e^(i9d-^bb+hbqom'
app.secret_key = SECRET_KEY
app.config['PERMANENT_SESSION_LIFETIME'] = 60
# 定义认证装饰器
def login_required(func):
@wraps(func)
def decorated_function(*args, **kwargs):
if not 'username' in session:
abort(401)
return func(*args, **kwargs)
return decorated_function
def index():
response_data = {"code": 200, "msg": "首页", "data": None}
return jsonify(response_data)
def login():
response_data = {"code": 200, "msg": "登录成功", "data": None}
return jsonify(response_data)
def logout():
response_data = {"code": 200, "msg": "退出登录", "data": None}
return jsonify(response_data)
def user_info():
response_data = {"code": 200, "msg": "个人中心", "data": None}
return jsonify(response_data)
app.add_url_rule('/index', view_func=index, methods=["GET"])
app.add_url_rule('/login', view_func=login, methods=["POST"])
app.add_url_rule('/logout', view_func=logout, methods=["GET"])
app.add_url_rule('/user_info', view_func=login_required(user_info), methods=["GET"])
if __name__ == '__main__':
app.run()
(2) CBV
from functools import wraps
from flask import Flask, request, views, jsonify, session, abort
from random import choice
app = Flask(__name__)
app.config['DEBUG'] = True
# 在启用Session的时候,一定要有它
SECRET_KEY = '-67=d2cg8@d0zw+c268sj84s61yc$b_e2*e^(i9d-^bb+hbqom'
app.secret_key = SECRET_KEY
app.config['PERMANENT_SESSION_LIFETIME'] = 60
# 定义认证装饰器
def login_required(func):
@wraps(func)
def decorated_function(*args, **kwargs):
if not 'username' in session:
abort(401)
return func(*args, **kwargs)
return decorated_function
# 登录接口
class LoginView(views.MethodView):
methods = ["POST"]
decorators = []
def post(self):
response_data = {"code": 200, "msg": "success", "data": None}
request_data = request.get_json()
username = request_data.get('username', None)
password = request_data.get('password', None)
if not all([username, password]):
response_data["msg"] = "必填项不能为空"
if username == "admin" and password == "1234":
session['username'] = username
return jsonify(response_data)
# 退出登录接口
class LogoutView(views.MethodView):
methods = ["GET"]
decorators = [login_required, ]
def get(self):
response_data = {"code": 200, "msg": "success", "data": None}
session.pop('username', None)
return jsonify(response_data)
# 个人中心接口
class UserInfoView(views.MethodView):
methods = ["GET"]
decorators = []
def get(self):
name = session.get("username")
print(name)
response_data = {"code": 200, "msg": "success", "data": None}
return jsonify(response_data)
app.add_url_rule("/login", endpoint=None, view_func=LoginView.as_view(name="login"))
app.add_url_rule("/logout", endpoint=None, view_func=LogoutView.as_view(name="logout"))
app.add_url_rule("/user_info", endpoint=None, view_func=UserInfoView.as_view(name="user_info"))
if __name__ == '__main__':
app.run()
3、可插拔视图
(1) 视图类
案例一
app.py
from flask import Flask, render_template, views
app = Flask(__name__)
app.config['DEBUG'] = True
class HelloView(views.View):
def dispatch_request(self, name=None):
return render_template('hello-view.html', name=name)
view = HelloView.as_view('helloview')
app.add_url_rule('/helloview', view_func=view)
app.add_url_rule('/helloview/<name>', view_func=view)
if __name__ == '__main__':
app.run()
hello-view.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello-View</title>
</head>
<body>
<h1>Hello-View {{ name }}</h1>
</body>
</html>
我们创建了一个 flask.views.View 的子类,并覆盖了其 dispatch_request() 函数,渲染视图的主要代码必须写在这个函数里。然后我们通过 as_view() 方法把类转换为实际的视图函数,as_view() 必须传入一个唯一的视图名。此后,这个视图就可以由 app.add_url_rule() 方法绑定到路由上了。上例的效果,同本篇第一节中”/hello”路径的效果,完全一样。
这个例子比较简单,只是为了介绍怎么用视图类,体现不出它的灵活性,我们再看个例子:
案例二
app.py
from flask import Flask, render_template, views
app = Flask(__name__)
app.config['DEBUG'] = True
class RenderTemplateView(views.View):
def __init__(self, template):
self.template = template
def dispatch_request(self):
return render_template(self.template)
app.add_url_rule('/hello', view_func=RenderTemplateView.as_view('hello', template='hello-view.html'))
app.add_url_rule('/login', view_func=RenderTemplateView.as_view('login', template='login-view.html'))
if __name__ == '__main__':
app.run()
hello-view.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello-View</title>
</head>
<body>
<h1>Hello-View</h1>
</body>
</html>
login-view.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Login-View</title>
</head>
<body>
<h1>Login-View</h1>
</body>
</html>
很多时候,渲染视图的代码都类似,只是模板不一样罢了。我们完全可以把渲染视图的代码重用,上例中,我们就省去了分别定义 hello 和 login 视图函数的工作了。
(2) 视图装饰器支持
在使用视图类的情况下,视图装饰器要怎么用呢?Flask在0.8版本后支持这样的写法:
from functools import wraps
from flask import Flask, request, views, jsonify, session, abort
from random import choice
app = Flask(__name__)
app.config['DEBUG'] = True
# 在启用Session的时候,一定要有它
SECRET_KEY = '-67=d2cg8@d0zw+c268sj84s61yc$b_e2*e^(i9d-^bb+hbqom'
app.secret_key = SECRET_KEY
app.config['PERMANENT_SESSION_LIFETIME'] = 60
# 定义认证装饰器
def login_required(func):
@wraps(func)
def decorated_function(*args, **kwargs):
if not 'username' in session:
abort(401)
return func(*args, **kwargs)
return decorated_function
# 登录接口
class LoginView(views.MethodView):
methods = ["POST"]
decorators = []
def post(self):
response_data = {"code": 200, "msg": "success", "data": None}
request_data = request.get_json()
username = request_data.get('username', None)
password = request_data.get('password', None)
if not all([username, password]):
response_data["msg"] = "必填项不能为空"
if username == "admin" and password == "1234":
session['username'] = username
return jsonify(response_data)
# 个人中心接口
class UserInfoView(views.MethodView):
methods = ["GET"]
decorators = [login_required, ]
def get(self):
name = session.get("username")
print(name)
response_data = {"code": 200, "msg": "success", "data": None}
return jsonify(response_data)
app.add_url_rule("/login", endpoint=None, view_func=LoginView.as_view(name="login"))
app.add_url_rule("/user_info", endpoint=None, view_func=UserInfoView.as_view(name="user_info"))
if __name__ == '__main__':
app.run()
我们只需将装饰器函数加入到视图类变量 decorators 中即可。它是一个列表,所以能够支持多个装饰器,并按列表中的顺序执行。
(3) 请求方法的支持
当我们的视图要同时支持GET和POST请求时,视图类可以这么定义:
class MyMethodView(View):
methods = ['GET', 'POST']
def dispatch_request(self):
if request.method == 'GET':
return '<h1>Hello World!</h1>This is GET method.'
elif request.method == 'POST':
return '<h1>Hello World!</h1>This is POST method.'
app.add_url_rule('/mmview', view_func=MyMethodView.as_view('mmview'))
我们只需将需要支持的HTTP请求方法加入到视图类变量 methods 中即可。没加的话,默认只支持GET请求。
(4) 基于方法的视图
from flask import Flask, views, jsonify
app = Flask(__name__)
app.config['DEBUG'] = True
class UserView(views.MethodView):
methods = ["GET", "POST", "PUT", "PATCH", "DELETE"]
decorators = []
def get(self, user_id):
response_data = {"code": 200, "msg": "我是 ResponseView 的 GET 方法", "data": None}
return jsonify(response_data)
def post(self):
response_data = {"code": 200, "msg": "我是 ResponseView 的 POST 方法", "data": None}
return jsonify(response_data)
def put(self, user_id):
response_data = {"code": 200, "msg": "我是 ResponseView 的 PUT 方法", "data": None}
return jsonify(response_data)
def patch(self, user_id):
response_data = {"code": 200, "msg": "我是 ResponseView 的 PATCH 方法", "data": None}
return jsonify(response_data)
def delete(self, user_id):
response_data = {"code": 200, "msg": "我是 ResponseView 的 DELETE 方法", "data": None}
return jsonify(response_data)
user_view = UserView.as_view('user_view')
# 将GET /users/请求绑定到UserAPI.get()方法上,并将get()方法参数user_id默认为None
app.add_url_rule('/user/', view_func=user_view, defaults={'user_id': None}, methods=['GET', ])
# 将POST /users/请求绑定到UserAPI.post()方法上
app.add_url_rule('/user/', view_func=user_view, methods=['POST', ])
# 将/users/<user_id>URL路径的GET,PUT,DELETE请求,
# 绑定到UserAPI的get(), put(), delete()方法上,并将参数user_id传入。
app.add_url_rule('/users/<user_id>', view_func=user_view, methods=['GET', 'PUT', 'DELETE'])
if __name__ == '__main__':
app.run()
如果API多,有人觉得每次都要加这么三个路由规则太麻烦,可以将其封装个函数:
def register_api(view, endpoint, url, primary_id='id', id_type='int'):
view_func = view.as_view(endpoint)
app.add_url_rule(url, view_func=view_func, defaults={primary_id: None}, methods=['GET', ])
app.add_url_rule(url, view_func=view_func, methods=['POST', ])
app.add_url_rule('%s<%s:%s>' % (url, id_type, primary_id), view_func=view_func, methods=['GET', 'PUT', 'DELETE'])
register_api(UserView, 'users', '/users/', primary_id='user_id')
4、延迟加载视图
当某一视图很占内存,而且很少会被使用,我们会希望它在应用启动时不要被加载,只有当它被使用时才会被加载。也就是接下来要介绍的延迟加载。Flask原生并不支持视图延迟加载功能,但我们可以通过代码实现。这里,我引用了官方文档上的一个实现。
Flask 装饰器
1、flask 基本装饰器
| 装饰器 | 说明 |
|---|---|
| @app.errorhandler(404) | 错误响应 |
| @app.before_first_request | 第一次请求之前执行 |
| @app.before_request | 每次请求之前执行 |
| @app.after_request | 每次请求之后执行 |
| @app.template_global() | 全局方法 |
| @app.template_filter() | 全局方法 |
| @app.route('/') | 路由 |
2、基本使用
from flask import Flask
app = Flask(__name__)
@app.errorhandler(404)
def page_not_found(error):
return 'This page does not exist', 404
@app.before_first_request
def before_first_request_func():
print("第一次请求之前执行...")
@app.before_request
def before_request_func():
print("每次请求之前执行...")
@app.after_request
def after_request_func(response):
print("每次请求之后执行...")
return response
@app.route('/index')
def index():
print('index')
return "index"
@app.route('/order')
def order():
print('order')
return "order"
if __name__ == '__main__':
app.run()
3、多个装饰器
from flask import Flask
app = Flask(__name__)
@app.before_first_request
def before_first_request_func():
print("只有第一次请求之前执行...")
@app.before_request
def before_request_func_1():
print("每次请求之前执行...1")
@app.before_request
def before_request_func_1():
print("每次请求之前执行...2")
@app.after_request
def after_request_func_1(response):
print("每次请求之后执行...1")
return response
@app.after_request
def after_request_func_2(response):
print("每次请求之后执行...2")
return response
@app.route('/index')
def index():
print('index')
return "index"
@app.route('/order')
def order():
print('order')
return "order"
if __name__ == '__main__':
app.run()
4、实现登录验证
from flask import Flask, request, session, redirect
app = Flask(__name__)
app.secret_key = "hd7y9&*^)*(G^#EG&GBG&WECUPBU(N0u0cj0cgGTF%(G&vb8yby8"
@app.before_request
def check_login():
if request.path == '/login':
return None
user = session.get('user_info')
if not user:
return redirect('/login')
@app.route('/login', methods=['GET', 'POST'])
def login():
print('login')
return "login"
@app.route('/index')
def index():
print('index')
return "index"
if __name__ == '__main__':
app.run()
5、实现全局异常捕获
from flask import Flask, jsonify
from werkzeug import exceptions
app = Flask(__name__)
app.config["DEBUG"] = True
# 方式二 分类
@app.route('/login')
def login():
return "login"
# 全局异常捕获
@app.errorhandler(Exception)
def exception(error):
response_data = {"code": 500, "msg": "服务器维护中", "data": None}
print(type(error))
print(error)
if isinstance(error, ValueError):
response_data["code"] = 400
response_data["msg"] = "请求值异常"
elif isinstance(error, exceptions.NotFound):
response_data["code"] = 404
response_data["msg"] = "Not Found"
return jsonify(response_data)
if __name__ == '__main__':
app.run()
Flask wtforms
作用: 用于对python web框架做表单验证。
安装:
pip install wtforms
pip install email_validator
1、用户登录
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from flask import Flask, render_template, request, redirect
from wtforms import Form
from wtforms.fields import core
from wtforms.fields import html5
from wtforms.fields import simple
from wtforms import validators
from wtforms import widgets
app = Flask(__name__, template_folder='templates')
app.debug = True
class LoginForm(Form):
name = simple.StringField(
label='用户名',
validators=[
validators.DataRequired(message='用户名不能为空.'),
validators.Length(min=6, max=18, message='用户名长度必须大于%(min)d且小于%(max)d')
],
widget=widgets.TextInput(),
render_kw={'class': 'form-control'}
)
pwd = simple.PasswordField(
label='密码',
validators=[
validators.DataRequired(message='密码不能为空.'),
validators.Length(min=8, message='用户名长度必须大于%(min)d'),
validators.Regexp(regex="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{8,}",
message='密码至少8个字符,至少1个大写字母,1个小写字母,1个数字和1个特殊字符')
],
widget=widgets.PasswordInput(),
render_kw={'class': 'form-control'}
)
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
form = LoginForm()
return render_template('login.html', form=form)
else:
form = LoginForm(formdata=request.form)
if form.validate():
print('用户提交数据通过格式验证,提交的值为:', form.data)
else:
print(form.errors)
return render_template('login.html', form=form)
if __name__ == '__main__':
app.run()
2、用户注册
from flask import Flask, render_template, request, redirect
from wtforms import Form
from wtforms.fields import core
from wtforms.fields import html5
from wtforms.fields import simple
from wtforms import validators
from wtforms import widgets
app = Flask(__name__, template_folder='templates')
app.debug = True
class RegisterForm(Form):
name = simple.StringField(
label='用户名',
validators=[
validators.DataRequired()
],
widget=widgets.TextInput(),
render_kw={'class': 'form-control'},
default='alex'
)
pwd = simple.PasswordField(
label='密码',
validators=[
validators.DataRequired(message='密码不能为空.')
],
widget=widgets.PasswordInput(),
render_kw={'class': 'form-control'}
)
pwd_confirm = simple.PasswordField(
label='重复密码',
validators=[
validators.DataRequired(message='重复密码不能为空.'),
validators.EqualTo('pwd', message="两次密码输入不一致")
],
widget=widgets.PasswordInput(),
render_kw={'class': 'form-control'}
)
email = html5.EmailField(
label='邮箱',
validators=[
validators.DataRequired(message='邮箱不能为空.'),
validators.Email(message='邮箱格式错误')
],
widget=widgets.TextInput(input_type='email'),
render_kw={'class': 'form-control'}
)
gender = core.RadioField(
label='性别',
choices=(
(1, '男'),
(2, '女'),
),
coerce=int
)
city = core.SelectField(
label='城市',
choices=(
('bj', '北京'),
('sh', '上海'),
)
)
hobby = core.SelectMultipleField(
label='爱好',
choices=(
(1, '篮球'),
(2, '足球'),
),
coerce=int
)
favor = core.SelectMultipleField(
label='喜好',
choices=(
(1, '篮球'),
(2, '足球'),
),
widget=widgets.ListWidget(prefix_label=False),
option_widget=widgets.CheckboxInput(),
coerce=int,
default=[1, 2]
)
def __init__(self, *args, **kwargs):
super(RegisterForm, self).__init__(*args, **kwargs)
self.favor.choices = ((1, '篮球'), (2, '足球'), (3, '羽毛球'))
def validate_pwd_confirm(self, field):
"""
自定义pwd_confirm字段规则,例:与pwd字段是否一致
:param field:
:return:
"""
# 最开始初始化时,self.data中已经有所有的值
if field.data != self.data['pwd']:
# raise validators.ValidationError("密码不一致") # 继续后续验证
raise validators.StopValidation("密码不一致") # 不再继续后续验证
@app.route('/register', methods=['GET', 'POST'])
def register():
if request.method == 'GET':
form = RegisterForm(data={'gender': 1})
return render_template('register.html', form=form)
else:
form = RegisterForm(formdata=request.form)
if form.validate():
print('用户提交数据通过格式验证,提交的值为:', form.data)
else:
print(form.errors)
return render_template('register.html', form=form)
if __name__ == '__main__':
app.run()
Flask 蓝图
小型应用程序:示例
大型应用程序:示例
Flask 认证
1、基于 session 验证
Flask 权限
Flask 节流
Flask 序列化
import json
from marshmallow import fields, post_load
from marshmallow_sqlalchemy import SQLAlchemySchema, SQLAlchemyAutoSchema, auto_field
from app.users.models import User
# json 序列化
class JsonSerializer(fields.Field):
def _serialize(self, value, attr, obj, **kwargs):
print(value)
try:
return json.loads(value)
except Exception:
return value
def _deserialize(self, value, attr, data, **kwargs):
return value
# 用户信息序列化器
class UserSchema(SQLAlchemyAutoSchema):
appointment_time = auto_field(format='%Y-%m-%d %H:%M:%S')
timeout = auto_field(format='%Y-%m-%d %H:%M:%S')
order_info = JsonSerializer()
class Meta:
model = User
# 启用外键关系
include_fk = False
# 模型关系外部属性
include_relationships = False
# 如果要全换全部字段,就不要声明fields或exclude字段即可
# fields = []
Flask 分页
# 分页模块(三种方式)
from collections import OrderedDict
from urllib.parse import urlencode
from app.afterservice.error import BeyondMaxValueError
class BasePagination:
pass
# A.创建自定义分页(根据页码进行分页)
class PageNumberPagination(BasePagination):
# 默认每页显示的数据条数
page_size = 15
# 获取URL参数中设置的每页显示数据条数
page_size_query_param = 'limit'
# 获取URL参数中传入的页码key
page_query_param = 'page'
# 最大支持的每页显示的数据条数
max_page_size = 50
def paginate_queryset(self, queryset, request, serializer=None, view=None):
"""
param queryset: 根据条件筛选出来的结果
param request: 请求
"""
# 获取请求参数
params = request.args
page = abs(int(params.get(self.page_query_param, 1)))
limit = abs(int(params.get(self.page_size_query_param, self.page_size)))
# if limit > self.max_page_size:
# raise BeyondMaxValueError
# <class 'app.afterservice.models.AfterSalesOrder'>
self.pagination = queryset.paginate(page, per_page=limit, error_out=False)
return OrderedDict([
('total', self.get_total()),
('next', self.get_next_link(request)),
('previous', self.get_previous_link(request)),
('page', self.get_current_page()),
('pages', self.get_pages()),
('data', self.serializer_data(serializer=serializer)),
('limit', limit)
])
# 获取下一页链接
def get_next_link(self, request):
if not self.pagination.has_next:
return None
params = dict(request.args)
params["page"] = self.pagination.next_num
result = urlencode(params)
return request.base_url + "?" + result
# 获取上一页链接
def get_previous_link(self, request):
if not self.pagination.has_prev:
return None
params = dict(request.args)
params["page"] = self.pagination.prev_num
result = urlencode(params)
return request.base_url + "?" + result
# 获取总条数
def get_total(self):
return self.pagination.total
# 获取当前页面页数
def get_current_page(self):
return self.pagination.page
# 获取总页数
def get_pages(self):
return self.pagination.pages
# 序列化数据
def serializer_data(self, serializer=None):
if serializer:
return serializer.dump(self.pagination.items, many=True)
data_list = []
for item in self.pagination.items:
del item.__dict__["_sa_instance_state"]
data_list.append(item.__dict__)
return data_list
# 实现单例模式
page_number_pagination = PageNumberPagination()
Flask 过滤
Flask Redis
1、基本使用
(1) app.py
from apps import create_app
app = create_app()
if __name__ == '__main__':
app.run()
(2) apps/_init_.py
from flask import Flask
from apps.api import api
from flask_sqlalchemy import SQLAlchemy
from flask_redis import FlaskRedis
db = SQLAlchemy()
redis = FlaskRedis()
# 创建app
def create_app():
app = Flask(__name__)
app.config.from_object('settings.DevelopmentConfig')
# 注册蓝图
app.register_blueprint(api, url_prefix=f"/api")
db.init_app(app)
redis.init_app(app)
return app
(3) utils/redis_utils.py
import pickle
from apps import redis
class Redis:
@classmethod
def write(self, key, value, expire=None):
"""
写入键值对
"""
redis.set(key, value, ex=expire)
@classmethod
def write_dict(self, key, value, expire=None):
'''
将内存数据二进制通过序列号转为文本流,再存入redis
'''
redis.set(pickle.dumps(key), pickle.dumps(value), ex=expire)
@classmethod
def read_dict(self, key):
'''
将文本流从redis中读取并反序列化,返回
'''
data = redis.get(pickle.dumps(key))
if data is None:
return None
return pickle.loads(data)
@classmethod
def read(self, key):
"""
读取键值对内容
"""
value = redis.get(key)
return value.decode('utf-8') if value else value
@classmethod
def hset(self, name, key, value):
"""
写入hash表
"""
redis.hset(name, key, value)
@classmethod
def delete(self, key):
return redis.delete(key)
redis_method = Redis()
(4) views.py
Flask 扩展
1、闪现
from flask import Flask, session
from flask import flash, get_flashed_messages
app = Flask(__name__)
app.secret_key = "bdc&HBV&SH97gnsuxb97GH(Hx8jxig&T%W(*hiuhgx99ini"
# 方式一 基于session去做
"""
@app.route('/login')
def login():
# 设置
session["msg"] = "xxxxxx"
return "login"
@app.route('/index')
def index():
# 阅后即焚,下次在拿的时候就没有了
msg = session.pop('msg')
print(msg)
return "index"
"""
# 方式二 基于 flash, get_flashed_messages (本质放也是基于 session 来实现的)
"""
@app.route('/login')
def login():
# 设置
flash("少时诵诗书所所所所所")
return "login"
@app.route('/index')
def index():
# 阅后即焚,下次在哪的时候就没有了
data = get_flashed_messages()
print(data)
return "index"
"""
# 方式二 分类
@app.route('/login')
def login():
# 设置
flash("少时诵诗书所所所所所", category='x1')
flash("嘻嘻嘻嘻嘻嘻嘻嘻信息", category='x2')
return "login"
@app.route('/index')
def index():
# 阅后即焚,下次在哪的时候就没有了
data = get_flashed_messages(category_filter=['x1', 'x2'])
print(data)
return "index"
if __name__ == '__main__':
app.run()
2、中间件
from flask import Flask
app = Flask(__name__)
@app.route('/login')
def login():
return "login"
@app.route('/index')
def index():
return "index"
class Middleware(object):
def __init__(self, old_wsgi_app):
"""
服务端启动时自动执行
:param old_wsgi_app:
"""
self.old_wsgi_app = old_wsgi_app
def __call__(self, *args, **kwargs):
"""
每次有请求到来的时候执行
def __call__(self, environ, start_response):
obj = self.old_wsgi_app(environ, start_response)
:param args:
:param kwargs:
:return:
"""
print('before')
obj = self.old_wsgi_app(*args, **kwargs)
print('after')
return obj
if __name__ == '__main__':
app.wsgi_app = Middleware(app.wsgi_app)
app.run()
"""
1. app.__call__()
2. app.wsgi_app()
"""
3、信号
Flask框架中的信号基于blinker,其主要就是让开发者可是在flask请求过程中定制一些用户行为。
安装
pip install blinker
(1) 内置信号
| 信号 | 说明 |
|---|---|
| request_started = _signals.signal('request-started') | 请求到来前执行 |
| request_finished = _signals.signal('request-finished') | 请求结束后执行 |
| before_render_template = _signals.signal('before-render-template') | 模板渲染前执行 |
| template_rendered = _signals.signal('template-rendered') | 模板渲染后执行 |
| got_request_exception = _signals.signal('got-request-exception') | 请求执行出现异常时执行 |
| request_tearing_down = _signals.signal('request-tearing-down') | 请求执行完毕后自动执行(无论成功与否) |
| appcontext_tearing_down = _signals.signal('appcontext-tearing-down') | 应用上下文执行完毕后自动执行(无论成功与否) |
| appcontext_pushed = _signals.signal('appcontext-pushed') | 应用上下文push时执行 |
| appcontext_popped = _signals.signal('appcontext-popped') | 应用上下文pop时执行 |
| message_flashed = _signals.signal('message-flashed') | 调用flask在其中添加数据时,自动触发 |
(2) 源码示例
request_started
from flask import Flask
flask.full_dispatch_request
(3) 自定义信号
from flask import Flask, current_app, flash, render_template
from flask.signals import _signals
app = Flask(import_name=__name__)
# 自定义信号
xxxxx = _signals.signal('xxxxx')
def func(sender, *args, **kwargs):
print(sender)
# 自定义信号中注册函数
xxxxx.connect(func)
@app.route("/x")
def index():
# 触发信号
xxxxx.send('123123', k1='v1')
return 'Index'
if __name__ == '__main__':
app.run()
4、上下文管理
6、session
(1) 配置
方式一:
方式二:
方式三:
from flask import Flask
app = Flask(__name__)
app.config['DEBUG'] = True
app.config['PERMANENT_SESSION_LIFETIME'] = 60
# 在启用Session的时候,一定要有它
SECRET_KEY = '-67=d2cg8@d0zw+c268sj84s61yc$b_e2*e^(i9d-^bb+hbqom'
app.secret_key = SECRET_KEY
(2) 设置 session
from flask import Flask, session
import os
app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(24)
# 设置session
@app.route('/')
def set():
# 设置“字典”键值对(正式开发时候,值需要session.get('user')获取)
session['username'] = 'admin'
return 'success'
if __name__ == '__main__':
app.run()
(3) 读取 session
因为session就像字典一样所以,操作它的时候有两种方法:
- result = session[‘key’] :如果内容不存在,将会报异常
- result = session.get(‘key’) :如果内容不存在,将返回None(推荐用法)
from flask import Flask, session
import os
app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(24)
# 设置session
@app.route('/')
def set():
# 设置“字典”键值对
session['username'] = 'admin'
return 'success'
# 读取session
@app.route('/get')
def get():
# session['username']
# session.get('username')
return session.get('username')
if __name__ == '__main__':
app.run()
(4) 删除 session
from flask import Flask, session
import os
app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(24)
# 设置session
@app.route('/')
def set():
session['username'] = 'admin'
return 'success'
# 读取session
@app.route('/get/')
def get():
# session['username']
# session.get('username')
return session.get('username')
# 删除session
@app.route('/delete/')
def delete():
print(session.get('username'))
session.pop('username', None)
# session['username'] = False
print(session.get('username'))
return 'success'
if __name__ == '__main__':
app.run()
(5) 清除session
from flask import Flask, session
import os
app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(24)
# 设置session
@app.route('/')
def set():
session['username'] = 'liefyuan'
return 'success'
# 读取session
@app.route('/get')
def get():
# session['username']
# session.get('username')
return session.get('username')
# 删除session
@app.route('/delete')
def delete():
print(session.get('username'))
session.pop('username')
# 或者
# session['username'] = False
print(session.get('username'))
return 'success'
# 清除session中所有数据
@app.route('/clear')
def clear():
print(session.get('username'))
# 清除session中所有数据
session.clear()
print(session.get('username'))
return 'success'
if __name__ == '__main__':
app.run()
(6) 设置 session 过期时间
方式一:
如果没有指定session的过期时间,那么默认是浏览器关闭后就自动结束。session.permanent = True在flask下则可以将有效期延长至一个月。下面有方法可以配置具体多少天的有效期。
- 如果没有指定session的过期时间,那么默认是浏览器关闭后就自动结束
- 如果设置了session的permanent属性为True,那么过期时间是31天。
- 可以通过给 app.config 设置 PERMANENT_SESSION_LIFETIME 来更改过期时间,这个值的数据类型是 datetime.timedelay 类型。
使用的需求:
- 在登录网页界面,下面有一个“记住我”选项,如果点击了则设置session的有效期长一点。就是设置这个!
@app.route('/')
def set():
session['username'] = 'admin'
# 长期有效,一个月的时间有效
session.permanent = True
return 'success'
方式二:
一种更先进的配置有效期的方法:(比如配置7天有效)
- 1.引入包:from datetime import timedelta
- 2.配置有效期限:app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7) # 配置7天有效
- 3.设置:session.permanent = True
from flask import Flask, session
from datetime import timedelta
import os
app = Flask(__name__)
app.config['SECRET_KEY'] = os.urandom(24)
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7) # 配置7天有效
# 设置session
@app.route('/')
def set():
session['username'] = 'liefyuan'
session.permanent = True
return 'success
7、多 app 应用
8、离线脚本
9、参考文档
浙公网安备 33010602011771号