FLask框架本身只实现了最基本的功能,所以FLask被称为 microFramework(微框架),
但是从工程上来看,这种所谓的“小”,反而带来了更多的便利性。
和Django这种完善完整高集成框架比起来,它很多东西都没有。
比如数据库database,template等等。需要自己安装相关的包,
不过优势就是,它只有一个基础的框架,想添加什么东西都是按照自己的意愿,
而且flask默认的模板渲染引擎是jinja2,这个可比Django(虽然也可以换成jinja2)自带的好用多了
pip install flask
from flask import Flask
def create_app():
# 树根
app = Flask(__name__)
@app.route("/test", methods=['POST', 'GET'])
def test():
return """See you!
"""
return app
app = create_app()
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5001)
有多种方式可以实现项目配置,这里介绍最常用的方式: python类路径方式导入
# 公共配置
class Config(object):
PUBLIC_FIELD = '1234'
BUCKET_NAME = 'dameinv'
# 开发环境
class DevelopmentConfig(Config):
DEBUG = True
HOST = localhost
PORT = 8888
# 配置日志
# LOG_LEVEL = "DEBUG"
LOG_LEVEL = "INFO"
REDIS_HOST = 'server-ip'
REDIS_PORT = 6380
REDIS_DB = 4
REDIS_PASSWORD = '×××××××××××'
MYSQL_INFO = "mysql://××××××××××@server-ip:3306/blog01?charset=utf8"
# 测试环境
class TestingConfig(Config):
TESTING = True
# 生成环境
class ProductionConfig(Config):
DEBUG = False
REDIS_HOST = 'server-ip'
REDIS_PORT = 6380
REDIS_DB = 4
REDIS_PASSWORD = '×××××××××××'
MYSQL_INFO = "mysql://××××××××××@server-ip:3306/blog01?charset=utf8"
# 指定项目使用哪个环境下的配置
Conf = DevelopmentConfig
from flask import Flask
from config.configs import Conf
def create_app():
app = Flask(__name__)
# 将配置信息注册到app上
app.config.from_object(Conf)
return app
app = create_app()
if __name__ == '__main__':
app.run(debug=app.config['DEBUG'], host=app.config['HOST'], port=app.config['PORT'])
from config.configs import Conf
@api.route('/')
def hello_world():
print(Conf.USER_NAME)
return 'Hello World!'
flask 的默认配置参数
{
'DEBUG': get_debug_flag(default=False), 是否开启Debug模式
'TESTING': False, 是否开启测试模式
'PROPAGATE_EXCEPTIONS': None,
'PRESERVE_CONTEXT_ON_EXCEPTION': None,
'SECRET_KEY': None,
'PERMANENT_SESSION_LIFETIME': timedelta(days=31),
'USE_X_SENDFILE': False,
'LOGGER_NAME': None,
'LOGGER_HANDLER_POLICY': 'always',
'SERVER_NAME': None,
'APPLICATION_ROOT': None,
'SESSION_COOKIE_NAME': 'session',
'SESSION_COOKIE_DOMAIN': None,
'SESSION_COOKIE_PATH': None,
'SESSION_COOKIE_HTTPONLY': True,
'SESSION_COOKIE_SECURE': False,
'SESSION_REFRESH_EACH_REQUEST': True,
'MAX_CONTENT_LENGTH': None,
'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12),
'TRAP_BAD_REQUEST_ERRORS': False,
'TRAP_HTTP_EXCEPTIONS': False,
'EXPLAIN_TEMPLATE_LOADING': False,
'PREFERRED_URL_SCHEME': 'http',
'JSON_AS_ASCII': True,
'JSON_SORT_KEYS': True,
'JSONIFY_PRETTYPRINT_REGULAR': True,
'JSONIFY_MIMETYPE': 'application/json',
'TEMPLATES_AUTO_RELOAD': None,
}
flask 的 路由 以 路由处理函数 的 装饰器 的形式管控着整个请求的导流,
这样处理的好处是简化了 路由注册的繁琐步骤。
from flask import Flask
def create_app():
app = Flask(__name__)
# 路由1
@app.route("/aaa", methods=['GET])
def aaa():
return """See you!
"""
# 路由2
@app.route("/bbb", methods=['POST'])
def bbb():
return """bbb"""
# 路由3
@app.route("/ccc", methods=['POST', 'GET])
def ccc():
return """ccc"""
return app
app = create_app()
把一个应用分解为一套蓝图。
这是针对大型应用的理想方案:一个项目可以实例化 一个应用,初始化多个扩展,并注册许多蓝图。
在一个应用的 URL 前缀和(或)子域上注册一个蓝图。
URL 前缀和(或)子域的 参数成为蓝图中所有视图的通用视图参数(缺省情况下)。
├── app_1_0 # 蓝图1号
│ ├── handfunc111.py # 路由处理模块一
│ ├── handfunc222.py # 路由处理模块一
│ └── __init__.py
│
├── app_2_0 # 蓝图1号
│ ├── __init__.py
│ └── handfunc111.py # 路由处理模块一
│
├── config
│ └── configs.py
│
└── run.py # 蓝图注册地点、
from flask import Flask
def create_app():
app = Flask(__name__)
# 注册 蓝图1号
from app_1_0 import api as api_v1
app.register_blueprint(api_v1, url_prefix='/api/v1') # /api/v1 是蓝图1号的 路由前缀
# 注册 蓝图2号
from app_2_0 import api as api_v2
app.register_blueprint(api_v2, url_prefix='/api/v2')
return app
app = create_app()
if __name__ == '__main__':
app.run(debug=app.config['DEBUG'], host=app.config['HOST'], port=app.config['PORT'])
# coding:utf-8
from flask import Blueprint
api = Blueprint('api', __name__)
from . import handfunc111,handfunc222 # 这条语句,放在最后!!!
# coding:utf-8
from flask import Flask, request, jsonify
from . import api
# 访问路由: /api/v1/aaa
@api.route('/aaa', methods=['GET])
def aaa():
return 'aaa!'
@api.route('/ccc', methods=['POST])
def wtt():
return 'ccc!'
from flask import Blueprint
api = Blueprint('api2_0', __name__)
from . import handfunc111 # 这条语句,放在最后!!!
from flask import Flask
def create_app():
# 默认静态目录 为工程目录下的 static目录,
# 可以 如下 手动制定:
app = Flask(__name__, static_folder='./wtt')
return app
app = create_app()
if __name__ == '__main__':
app.run(debug=app.config['DEBUG'], host=app.config['HOST'], port=app.config['PORT'])
from flask import Flask, redirect
@app.route("/who")
def hello():
return redirect('/wtt', 302) # 重定向到 /wtt
@app.route("/wtt")
def hello123():
return """See you!
"""
非同名钩子的 介绍顺序 就是 执行顺序
同名钩子的执行顺序 是从上到下
绑定到 app上的 钩子是 全局钩子, 绑定到 蓝图上的钩子是 局部钩子。
第一个请求被触发是 被调用,
一般用于初始化工作 和 调用 处理一些定时任务 的函数
。
每次 试图函数执行前 被调用
一般用于实现请求准备工作, 如参数校验,黑名单过滤,数据统计
每次 试图函数执行后 被调用
一般用于实现响应的 加工工作, 如 设置统一的请求头,格式转换
每次请求销毁(after_request 执行)后 被调用, 用来记录服务器的异常信息后
无论是否出现一场都会触发,
一般用于 请求的收尾工作, 如资源回收, 错误统计
app = Flask(__name__)
CORS(app,supports_credentials=True) # 跨域处理
app.config.from_object(ConfigData) # 将配置信息注册到app上
# 局部钩子 没有 before_first_request
# 'Blueprint' object has no attribute 'before_first_request'
# @v2.before_first_request
# def asdfzxcvsdf():
# print('222 before_first_request')
@app.before_first_request
def asdfzxcvsdf():
print('before_first_request')
@app.before_request
def onnect123444():
print('before_request')
@app.after_request
def gguest(resp):
print("after_request")
return resp
@app.teardown_request
def dfgosdfgdfe(exc):
print('teardown_request')
@app.before_request
def aaa():
print('''before_request''')
# # abort 可以 禁止 路由处理函数的执行, 接着让 after_request执行
# res = make_response("""See You!
""")
# abort(res)
# # # 用 return 也可以 禁止 路由处理函数的执行, 接着让 after_request执行
# res = make_response("""See You!
""")
# return res
pip3 install flask-cors
通过给路由添加@cross_origin标识即可
from flask_cors import cross_origin
@app.route('/index', methods=['POST', 'OPTIONS'])
@cross_origin()
def index():
result_text = {"result": "True"}
return jsonify(result_text)
根据路由正则来批量控制等方式,更加灵活,可以查阅官方文档。
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app, resources={r"/api/*": {"origins": "*"}},supports_credentials=True)
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app,supports_credentials=True)
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)
蓝图1号: init.py
# coding:utf-8
from flask import Blueprint
api = Blueprint('api', __name__)
from . import handfunc111,handfunc222 # 这条语句,放在最后!!!
# coding:utf-8
from flask import Blueprint
from db.mysql import database
# from flask import Flask, request as r
# 蓝图1号: 分支一
v1 = Blueprint('v1', __name__)
@v1.before_request
def _db_connect():
database.connect()
@v1.teardown_request
def _db_close(exc):
if not database.is_closed():
database.close()
from . import handfunc111,handfunc222 # 这条语句,放在最后!!!
请求报文封装在了flask 的 reques模块, 使用前先导入
from flask import Flask, request
# 访问 aaa?name=tom&age=10
@app.route("/aaa")
def hello():
# way1
name = request.args['name'] # 如果url中没有name参数,则会报错!!!
age = request.args['age']
#way2
name = request.args.get('name') # 如果url中没有name参数,则 name = None
age = request.args.get('age')
return "ok"
### form-data
@app.route("/aaa")
def hello():
# way1
name = request.form['name'] # 如果请求体中没有name参数,则会报错!!!
age = request.form['age']
#way2
name = request.form.get('name') # 如果请求体中没有name参数,则 name = None
age = request.form.get('age')
return "ok"
### JSON 等原始数据
import json
@app.route("/aaa")
def hello():
# way1
indata = json.loads(request.data)
return "ok"
### 文件数据
import uuid
@app.route("/aaa")
def hello():
upload_file = request.files["file"]
filePath = str(uuid.uuid4()) + upload_file.filename
upload_file.save(filePath)
return res_success(filePath)
### 返回json数据
return {
"code":0,
"data": "Ok",
}
### 返回普通字符串
return "123"
### 返回HTML片段
return """See you!
"""
### 返回文件流
filePath = "/hero/static/"
fileName = "好音乐.mp3"
return send_from_directory(directory=filePath,filename=fileName,path= filePath+fileName, as_attachment=True)
from flask import Flask, request, make_response
# make_response 相应报文 制造者
@app.route("/")
def hello():
resp = make_response({
"name": "tom",
"age": 11
})
resp.headers ['name'] ='tom'
resp.set_cookie("key", "value")
return resp
直接使用 python3 run.py 运行服务的方式只适合本地开发。
线上运行时要保证更高的性能和稳定性,我们需要使用 uwsgi 进行部署。
uwsgi --socket 0.0.0.0:5000 --protocol=http -p 3 -w run:app
--socket 0.0.0.0:5000:指定暴露端口号为5000。
--protocol=http:说明使用 http 协议,即端口5000可以直接使用HTTP请求进行访问。
-p 3表示启动的服务占用3个进程。
-w run:app:-w 指明了要启动的模块,run 就是项目启动文件 run.py 去掉扩展名,app 是 run.py 文件中的变量 app,即 Flask 实例。
- 安装
pip3 install uwsgi
cd 到 uwsgi.ini 所在目录下
- 启动:
uwsgi --ini uwsgi.ini
- 重启:
uwsgi --reload uwsgi.pid
- 停止:
uwsgi --stop uwsgi.pid
uwsgi.ini
[uwsgi]
# 四元组配置:
# 和 nginx 配合使用时 使用 socket 配置
#socket=0.0.0.0:8000
# 直接作为web服务器使用时 使用 http 配置,就不必启用 socket配置了
http=0.0.0.0:8010
# 配置工程目录(入口py文件 的 pwd)
chdir = /home/hero/Desktop/testFlask
# module相当于之前命令行中的-w参数,指明了在 工程目录下 要启动的模块,
# run 就是项目启动文件 run.py 去掉扩展名,
# app 是 run.py 文件中的变量 app,即 Flask 实例
module = run:app
#配置进程,线程信息
master=True
processes=1
threads=8 #最好和核心数保持一致,可以减少 线程切换的资源损耗
buffer-size = 32768
# 守护进程 方式的启动, 并且 记录 守护进程 方式的启动 的 日志记录
daemonize=uwsgi.log
# 记录守护进程 的 id
pidfile=uwsgi.pid
# 非 守护进程 方式的启动 的 日志记录
# logto = /home/hero/Desktop/testFlask/myproject.log
说明:
# 如果要采用uwsgi 启动,
app.run(debug=app.config['DEBUG'], host=app.config["HOST"], port=app.config["PORT"])
# 则需将上行代码改为下面:
app.run()
Peewee是一种简单而小的ORM。它有一种小的、有表现力的形式, 思想上很切合flask的主张。
安装
# 安装驱动
pip install pymysql
# 安装orm
pip install peewee
现有数据表 反射 为 类模型
python3 -m pwiz -e mysql -u root -H localhost --password music_level > Model.py
from db import database, Test,db
# 推荐: 添加数据, 使用字典, 且不能有 主键
p = {'name':"tantan", 'age' : "100", 'aaa': "123"}
p_id = Test.insert(p).execute()
print(p_id) # 被 主键值 回显
# 数据以参数 的形式添加,且不能有 主键, 返回主键值
p = Test.create(name = "123", uid = 60)
print(p)
# 根据主键,有则更新,无则添加
p = Test(uid =44, name= "tantan", age = "100", aaa= "123")
res = p.save() # p 被 主键值 回显, res 是行数
print(p,res)
# 使用事务, 插入 多条 数据
data = [{'name': 'tom', 'age': i} for i in range(10)]
with database.atomic():
for i in range(10):
Test.insert_many(data[i]).execute()
# 删除数据
res = Test.delete().where(Test.uid >= 45).execute()
print(res) # 删除行数
from playhouse.shortcuts import model_to_dict
# 查询 一条 数据 的 主键值, 找不到 则 报错: NotExist
p = Test.get(Test.age == 100)
print(p) # 主键id
print(p.name) # 查询记录的 name字段值
# 查询的结果都是该 Model 的 object,注意不是 dict。
# 如果想让结果为 dict,需要 playhouse.shortcuts 模块的 model_to_dict工具方法进行转化
resMap = model_to_dict(p)
print(resMap) # {'uid': 44, 'aaa': 101, 'age': '100', 'name': 'haha+++'}
# 查询 一条 数据, 没有数据返回None
p = Test.get_or_none(Test.age == 100)
print(p==None)
print(p) # 默认展示的是 主键值
# 查询 多条 数据
ps = Test.select(Test.uid,Test.aaa).where(Test.name == "tom")
print(ps) # 默认展示的是 sql语句
results = [model_to_dict(item) for item in ps]
for p in results:
print(p)
# 更新数据
p = Test.update({
Test.aaa:101,
Test.name: "haha+++"
}).where(Test.name == "tantan").execute()
print(p) # 返回更新记录数目
# 推荐:字段值的 ++ --
updates = {
"uid": Bbb.uid - 10,
"name": "tom",
}
Bbb.update().where(Bbb.id == 1).execute()
# 获取数量
total = Test.select().where(Test.name=='tom').count()
print(total)
# 排序
ps = Test.select(Test.uid,Test.aaa).where(Test.name == "tom").order_by(Test.uid.desc())
# 分页
ps = Test.select(Test.uid,Test.aaa).where(Test.name == "tom").order_by(Test.uid.desc()).limit(3).offset(2)
# like
sql = f"(Test.name % '%{indata['name']}%')"
ps = Test.select().where(eval(sql))
# in
sql = f"(Test.name << ['tom', 'cat'])"
# 且
sql = f"(Test.name == 'tom') & (Test.age == 10)"
# 或
sql = f"(Test.name == 'tom') | (Test.age == 10)"
datas = AAA.select(AAA.uid, BBB.name).join(BBB, on=(AAA.exam_id==BBB.id)).where()
for item in datas.objects():
print(item.uid)
print(item.name)
'''
如果不加 .objects(),只能获取到 表AAA 的数据,
而我们拼表的目的往往就是为了获取 表BBB 相关信息,这从思维上将应该是peewee的bug。
注意:
在使用[model_to_dict(item) for item in datas.objects()] 即使加上了objects()也只是显示 AAA的数据表结构的数据。
'''
# 当事务执行成功之后,它会自动commit(),不需要我们手动调。
# 当事务的代码块中抛出异常时,它会自动调用rollback(),
# 但是 错误只抛出一次,如果我们 用 try...expect 捕捉了,他就捕捉不到异常,
# 就无法自动调用rollback(),需要我们手动调用自动调用rollback()
with database.atomic() as tx:
try:
Test.create(name = "123")
Test.create(name = "123")
Test.create(name = "123", uid = 50)
except Exception as e:
print(e)
print(123123)
tx.rollback()
offset = (indata['curpage']-1)*10
limt = 10
keys = ('stat','total_fee','uid', 'id')
select = ''
for key in keys:
if select == '':
select = select + "a."+key + ' '
else:
select = select + ', a.' + key + ' '
sql = f"from `order` a join `user` b on a.uid = b.uid where b.exam_area='{indata['examarea']}' "
if 'stat' in indata:
sql += f"and a.stat = {indata['stat']} "
sql_find = f"select {select} "
sql_count = "select count(*) "
limt = f"limit {offset}, {limt}"
ret = database.execute_sql(sql_find+sql+limt)
res = ret.fetchall()
outdata = []
for item in res:
m = {}
i = 0
for key in keys:
m[key] = item[i]
i += 1
outdata.append(m)
ret = database.execute_sql(sql_count+sql)
total = ret.fetchone()[0]
page = page_handle(total, indata['curpage'])
return res_success({
'page': page,
'list': outdata,
})