Flask框架学习笔记
flask
1. day01
Django:
Model ORM Session
MouldForm
admin
组件大而全,浪费资源
Flask:
Session
小而精,三方组件全
稳定性相对较差
Tornado:
异步IO 非阻塞 原生websocket
干净
Sanic:
基于flask和tornado
1.Flask Response
from flask import Flask
app = Flask(__name__)
app.run()
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "Hello World"
app.run()
#Flask Response
HttpResponse: 返回字符串至客户端
return "Hello World"
render_template:与django中的render使用一致,返回模板由浏览器渲染
from flask import reder_template
return render_template("login.html")
redirect:跳转,重定向URL
from flask import redirect
return redirect("/login")#302
2.Flask 特殊
#Flask 特殊
jsonify:返回json标准的字符串 Content-Type:application/json
from flask import jsonify
return jsonify()
send_file:打开文件并返回文件内容(自动识别文件格式)
from flask import send_file
return send_file(path)
url_for():函数是用于构建指定函数的URL。
return redirct(url_for("蓝图对象里设置的name.函数名"))#蓝图中使用url_for()
3.request
django中的request
def django(req):
return HttpResponse("hello")
#flask中的request
http请求8种:get post delete put
from flask import request
request.method:请求方式
request.form:存放的FormData中的数据 .to_dict序列化成字典
.to_dict():转字典
.get():获取数据
request.args:获取URL中的数据 .to_dict序列化成字典
request.url:访问的完整路径
request.path:路由地址
request.host:主机地址ip+端口号
request.host_url:主机地址访问路径
request.json:如果提交时请求头中的Content-Type:application/json 字典操作
request.data:如果提交时请求头中的Content-Type无法被识别 将请求体中的原始数据存放 byte
request.values:获取FormData and URL中的数据,不要用.to_dict。查看数据,通常不用来获取数据
request.files:序列化文件存储 .save()
request.cookies:获取Cookie中的数据
request.headers:获取请求头
4.Jinja2
#Jinja2
{{}}:引用变量 执行函数
{%%}:逻辑代码
|safe Markup:安全标签字符串
@app.template_global("name"):全局模板函数 {{ name(a,b) }}
@app.template_filter("name"):自定义过滤器 {{ (a,b) | name }}
{% macro create_input(na,ty) %}
{{ na }} : <input type="{{ ty }}" name="{{ na }}">
{% endmacro %}
{{ create_input("username","text") }}
5.Session
#flask中的Session
app.secret_key = "加密字符串":用于序列化和反序列化session信息
由于flask中默认session存放位置 - 客户端的cookies中
所以session需要加密 用到secret_key
请求进入视图函数 带上cookie 将session从cookie序列化出来 通过secret_key 反序列化成字典
作业:
基于session装饰器,装饰至少两个视图函数
endpoint 默认视图函数名
@functools.wraps(装饰函数)
2. day02
回顾
from flask import Flask
app = Flask(__name__)
app.run("0.0.0.0",5000,debug=True)
#路由
@app.route("/",methods=["GET","POST"])
def index():
return
#Flask Response
HttpResponse return "str"
from flask import render_template
render return render_template("path")
from flask import redrict
redrict return redrict("route address")
#Flask 特殊
from flask import jsonify
jsonify return jsonify({"name":"v1"})#响应头中加入Content-Type:application/json
from flask import send_file
send_file return send_file("file path")
#打开文件并返回文件内容会自动识别文件类型Content-Type:文件类型(二进制文件时背景特殊颜色)
#flask中的Request
#公共变量
from flask import request
request.method#获取当前请求的方式
request.form#获取FormData中的数据 to_dict()字典操作
request.args#获取url中的数据 to_dict()字典操作
request.json#请求头当中Content-Type:application/json将数据序列化到json中
request.data#Content-Type无法被识别的时候,b""原始请求数据
request.files#获取文件数据,save("文件名")
request.values#查看form和url中的数据 不要使用 to_dict()会覆盖form中的数据
#Jinja2
{{}}#应用变量执行函数
{%%}#逻辑代码中使用
#flask中的Session
#公共变量
from flask import session
app.secret_key="str"
#存在cookie中的键值对,节省flask的开销
session["key"]="v1"#字典
KeyError
session.get("get")
1.flask中的路由
#flask中的路由
endpoint#反向生成url地址标志 默认视图函数名
methods#视图函数允许的请求方式
defaults={"nid":"123456"}#默认参数
strict_slashes=True#是否严格遵循路由地址 /index/
redirect_to="/login"#永久重定向 301
"/index/<int:page>"#动态路由参数
def index(page):
pass
2.flask实例化配置
#flask实例化配置
template_folder="tmp"#默认模板路径 templates
static_folder="static"#默认静态文件路由地址 static
static_url_path="/static"#访问静态文件路由地址 默认是"/"+static_folder
static_host=None#指定静态文件服务器地址
host_matching=False#如果真特别需要的话,慎用,否则所有的route都需要host=""的参数
subdomain_matching=False#理论上来说是用来显示SERVER_NAME子域名的
instance_path=None#指向另一个Flask实例的路径
instance_relative_config=False#是否加载另一个实例的配置
root_path=None#主模块所在的目录的绝对路径,默认项目目录
3.flask对象配置
#flask对象配置app.=/app.config[]=
"DEBUG":True#是否开启Debug模式
"TESTING":False#是否开启测试模式
"SECRET_KEY":""#在启用flask内置Session的时候/开启flash,一定要有它
"PERMANENT_SESSION_LIFETIME":31#days,Session的生命周期 单位天 默认31天
"SESSION_COOKIE_NAME":"session"#在cookies中存放session加密字符串的名字
"SERVER_NAME":""#服务访问域名
class FlaskDebug(object):
DEBUG=True
SECRET_KEY="DEBUGmoshidesecret_key"
PERMANENT_SESSION_LIFETIME=7
SESSION_COOKIE_NAME="debug_session"
app.config.from_object(FlaskDebug)
4.flask中的flash
#flask中的flash
from flask import flash,get_flashed_messages
flash("message","tag")#写入flash tag是标签 (一次访问动作之后flash消失)
get_flashed_messages(category_filter=["error"])#读取flash 过滤flash tag
5.flask蓝图
#flask蓝图
Blueprint#当成一个不能被启动的app Flask实例
from flask import Blueprint,render_template
sapp=Blueprint("sapp",__name__,template_folder="apptemp",url_prefix="/blue")
@sapp.route("/sapp")
def sappfunc():
return render_template("sapp.html")
app.register_blueprint(views.sapp)
url_prefix="/blue"#url前缀
6.flask中的装饰器
#flask中的装饰器
#请求-》视图函数-》响应-》客户端
@app.before_request#请求进入视图函数之前
@app.after_request#响应返回客户端之前
#正常情况下流程:be1 - be2 - be3 - af3 - af2 -af1
#异常情况下流程:be1 - af3 - af2 -af1
@app.errorhandler(404)#重定义错误页面返回信息
def error404(error_info):#参数为报错信息
return
作业:
使用蓝图实现增删改查4个蓝图
- before_request
- after_request
- errorhandler
3. day03
回顾
#路由
endpoint#反向生成url url_for 默认视图函数名
methods#允许的请求方式 默认GET 多种请求["GET","POST"]
"/index/<int:page>"#动态路由参数
def index(page):
pass
strict_slashes=False#是否严格遵循路由地址 /index/ /index
redirct_to="/login"#永久重定向 301
defaults={nid:1}#默认参数
def index(nid):
pass
#Flask实例化配置
template_folder="templates"#默认模板存放目录
static_folder="static"#默认静态文件存放目录
static_url_path="/static_folder"#静态文件访问路径
url_prefix=""#蓝图url前缀
#Flask对象配置
app=Flask(__name__)
app.config.from_obj(class)
#蓝图
from flask import Blueprint
blue=Blueprint("blue",__name__)
@blue.route("/blue")
def bluefunc():
pass
app.register_blueprint(blue)
#特殊装饰器
before_request#在请求进入视图函数之前
@app.before_request
def be1():
pass
def be2():
pass
def be3():
pass
after_request#在响应返回客户端之前
@app.after_request#自下而上 执行
def af1():
pass
def af2():
pass
def af3():
pass
正常:be1 -> be2 -> be3 -> af3 -> af2 -> af1
异常:be1 -> af3 -> af2 -> af1
errorhandler#重定义错误信息
@app.errorhandler(404)
def error404(error_info):
pass
1.CBV
#CBV
from flask import views
class Login(views.MethodView):
methods=["GET","POST"]
decorators=[app.route]
def get(self):
print(url_for("my_login"))
return render_template("login.html")
def post(self):
return "登陆成功"
app.add_url_rule("/",view_func=Login.as_view("my_login"))
2.Flask-Session
#Flask-Session
#交由客户端保管机制 - 安全性相对较差 Cookie
#原生session 优势是不占用一点服务器的空间
#Flask-Session - DjangoSession 相对一样的数据
#redis <- session
#redis在内网中使用
pip install Flask-Session,redis
from flask_session import Session
app.config["SESSION_TYPE"]="redis"
app.config["SESSION_REDIS"]=Redis(host="ip",port="port",db=0)
Session(app)
session["key"]="value"
#Flask中的session 需要执行 session_interface - open_session
3.WTForms
#WTForms
#Django 中的 ModelForm
pip install WTForms
from wtforms.fields import simple,core
from wtforms import validators
from wtforms import Form
class LoginForm(Form):
usernmae=simple.StringField(
label="用户名",#标签标记
validators=[validators.DataRequired(message="用户名不能为空")],#校验条件
description="",#描述标记
id="user_name",#标签id
default=None,#默认值
widget=None,#默认组件(input type="text") 在StringField中已经被实例化了
render_kw=None#{"class":"my_login"}
)
@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(): # 对用户提交数据进行校验,form.data是校验完成后的数据字典
print("用户提交的数据用过格式验证,值为:%s"%form.data)
return "登录成功"
else:
print(form.errors,"错误信息")
return render_template("login.html",form=form)
#WTForms实例
from flask import Flask, render_template, request
from wtforms.fields import simple
from wtforms import Form
from wtforms import validators
from wtforms import widgets
app = Flask(__name__, template_folder="templates")
class LoginForm(Form):
'''Form'''
name = simple.StringField(
label="用户名",
widget=widgets.TextInput(),
validators=[
validators.DataRequired(message="用户名不能为空"),
validators.Length(max=8, min=3, message="用户名长度必须大于%(max)d且小于%(min)d")
],
render_kw={"class": "form-control"} # 设置属性生成的html属性
)
pwd = simple.PasswordField(
label="密码",
validators=[
validators.DataRequired(message="密码不能为空"),
validators.Length(max=18, min=4, message="密码长度必须大于%(max)d且小于%(min)d"),
validators.Regexp(regex="\d+", message="密码必须是数字"),
],
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(): # 对用户提交数据进行校验,form.data是校验完成后的数据字典
print("用户提交的数据用过格式验证,值为:%s" % form.data)
return "登录成功"
else:
print(form.errors, "错误信息")
return render_template("login.html", form=form)
if __name__ == '__main__':
app.run(debug=True, host="0.0.0.0", port=80)
#login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>登录</h1>
<form method="post">
<!--<input type="text" name="name">-->
<p>{{form.name.label}} {{form.name}} {{form.name.errors[0] }}</p>
<!--<input type="password" name="pwd">-->
<p>{{form.pwd.label}} {{form.pwd}} {{form.pwd.errors[0] }}</p>
<input type="submit" value="提交">
</form>
</body>
</html>
4.数据库连接池
#POOL数据库连接池
#创建数据库连接池
import pymysql
from dbutils.pooled_db import PooledDB,SharedDBConnection
POOL = PooledDB(
creator=pymysql, # 使用链接数据库的模块
maxconnections=6, # 连接池允许的最大连接数,0和None表示不限制连接数
mincached=2, # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
maxcached=5, # 链接池中最多闲置的链接,0和None不限制
maxshared=3, # 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
blocking=True, # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
maxusage=None, # 一个链接最多被重复使用的次数,None表示无限制
setsession=[], # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
ping=0,
# ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
host='127.0.0.1',
port=3306,
user='root',
password='123456',
database='food_db',
charset='utf8'
)
#使用数据库连接池中的链接
def func():
# 检测当前正在运行连接数的是否小于最大链接数,如果不小于则:等待或报raise TooManyConnections异常
# 否则
# 则优先去初始化时创建的链接中获取链接 SteadyDBConnection。
# 然后将SteadyDBConnection对象封装到PooledDedicatedDBConnection中并返回。
# 如果最开始创建的链接没有链接,则去创建一个SteadyDBConnection对象,再封装到PooledDedicatedDBConnection中并返回。
# 一旦关闭链接后,连接就返回到连接池让后续线程继续使用。
conn = POOL.connection()#建立连接
cursor = conn.cursor()#游标
cursor.execute('select * from user')#查询操作
result = cursor.fetchall()#查询结果
print(result)
conn.close()
#自制sqlhelper
class MySQLhelper(object):
def __init__(self, host, port, dbuser, password, database):
self.pool = PooledDB(
creator=pymysql, # 使用链接数据库的模块
maxconnections=6, # 连接池允许的最大连接数,0和None表示不限制连接数
mincached=2, # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
maxcached=5, # 链接池中最多闲置的链接,0和None不限制
maxshared=3,
# 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。
blocking=True, # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
maxusage=None, # 一个链接最多被重复使用的次数,None表示无限制
setsession=[], # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
ping=0,
# ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
host=host,
port=int(port),
user=dbuser,
password=password,
database=database,
charset='utf8'
)
def create_conn_cursor(self):#创建连接游标
conn = self.pool.connection()
cursor = conn.cursor(pymysql.cursors.DictCursor)
return conn,cursor
def fetch_all(self, sql, args):#查询操作
conn,cursor = self.create_conn_cursor()
cursor.execute(sql,args)
result = cursor.fetchall()
cursor.close()
conn.close()
return result
def insert_one(self,sql,args):#insert操作
conn,cursor = self.create_conn_cursor()
res = cursor.execute(sql,args)
conn.commit()
print(res)
conn.close()
return res
def update(self,sql,args):#update操作
conn,cursor = self.create_conn_cursor()
res = cursor.execute(sql,args)
conn.commit()
print(res)
conn.close()
return res
sqlhelper = MySQLhelper("127.0.0.1", 3306, "root", "123456", "user")
sqlhelper.fetch_all("select * from user where id=%s",(1))
# sqlhelper.insert_one("insert into user VALUES (%s,%s)",("jinwangba",4))
# sqlhelper.update("update user SET name=%s WHERE id=%s",("yinwangba",1))
4. day04
1.偏函数
from functools import partial
def mod( n, m ):
return n % m
mod_by_100 = partial( mod, 100 )
print mod( 100, 7 ) # 2
print mod_by_100( 7 ) # 2
from functools import partial
bin2dec = partial( int, base=2 )
print bin2dec( '0b10001' ) # 17
print bin2dec( '10001' ) # 17
hex2dec = partial( int, base=16 )
print hex2dec( '0x67' ) # 103
print hex2dec( '67' ) # 103
2.线程安全
import time
import threading
class Foo(object):
pass
foo =Foo()
def add(i):
foo.num=i
time.sleep(1)
print(foo.num,i)
for i in range(20):
th=threading.Thread(target=add,args=(i,))
th.start()
# 字典{}
import time
import threading
from threading import local
class Foo(local):
pass
foo=Foo()#foo=threading.local()
def add(i):
foo.num=i
time.sleep(1)
print(foo.num,i,threading.current_thread().ident)
for i in range(20):
th=threading.Thread(target=add,args=(i,))
th.start()
3.flask上下文
from flask import Flask,request
app=Flask(__name__)
app.run()
app.__call__()
app.wsgi_app
#flask源码
上下文:即语境,语意,在程序中可以理解为在代码执行到某个时刻,根据之前代码锁做的操作以及下文即将要执行的逻辑,可以决定在当前时刻下可以使用到的变量,或者可以做的事情。
Flask中有两种上下文:请求上下文(request context)和应用上下文(application context)。
Flask中上下文对象:相当于一个容器,保存了Flask程序运行过程中的一些信息。
1.application指的是当你调用app = flask(name)创建的这个对象app。
2.request指的是每次http请求发生时,WSGI server(比如gunicorn)调用Flask.call()之后,在Flask对象内部创建的Request对象; 3.application表示用于相应WSGI请求的应用本身,request表示没出http请求;
4.appliacation的生命周期大于request,一个application存活期间,可能发生多次http请求,所以,也就会有多个request;
请求上下文(request context):在Flask中,可以直接在视图函数中使用request这个独享进行获取先关数据,而request就是请求上下文的对象,保存了当前本次请求的相关数据,请求上线文对象有:request、session
request:封装了HTTP请求的内容,针对的是http请求。例如:user = request.args.get('user'),获取的是get请求的参数。
session:用来记录请求会话中的信息,针对的是用户信息。例如:session['name'] = user.id 科可以记录用户信息。还可以通过session.get('name')获取用户信息。
应用上下文(application context):它不是一直存在的,它只是request context中的一个对app的代理,所谓的local proxy。它的作用主要是帮助request获取当前的应用,它是伴request而生,随request而灭的。
应用上下文对象有:current_app,g
current_app:应用程序上下文,用于存储应用程序中的变量,可以通过current_app.name打印当前app的名称,也可以在current_app中存储一些变量,例如:
应用的启动脚本是哪个文件,启动时指定了哪些参数
加载了哪些配置文件,导入了哪些配置
连接了哪个数据库
有哪些可以调用的工具类、常量
当前flask应用在哪个机器上,哪个IP上运行,内存多大
current_app.name
current_app.test_value='value'
g变量:g 作为 flask 程序全局的一个临时变量,充当者中间媒介的作用,我们可以通过它传递一些数据,g 保存的是当前请求的全局变量,不同的请求会有不同的全局变量,通过不同的thread id区别
g.name='abc'
注意:不同的请求,会有不同的全局变量
两者的区别:
请求上下文:保存了客户端和服务器交互的数据
应用上下文:flask 应用程序运行过程中,保存的一些配置信息,比如程序名、数据库连接、应用信息等
4.websocket
#websocket
import socket, base64, hashlib
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('127.0.0.1', 9527))
sock.listen(5)
# 获取客户端socket对象
conn, address = sock.accept()
# 获取客户端的【握手】信息
data = conn.recv(1024)
print(data)
"""
b'GET /ws HTTP/1.1\r\n
Host: 127.0.0.1:9527\r\n
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2\r\n
Accept-Encoding: gzip, deflate\r\n
Sec-WebSocket-Version: 13\r\n
Origin: http://localhost:63342\r\n
Sec-WebSocket-Extensions: permessage-deflate\r\n
Sec-WebSocket-Key: jocLOLLq1BQWp0aZgEWL5A==\r\n
Cookie: session=6f2bab18-2dc4-426a-8f06-de22909b967b\r\n
Connection: keep-alive, Upgrade\r\n
Pragma: no-cache\r\n
Cache-Control: no-cache\r\n
Upgrade: websocket\r\n\r\n'
"""
# magic string为:258EAFA5-E914-47DA-95CA-C5AB0DC85B11
magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
def get_headers(data):
header_dict = {}
header_str = data.decode("utf8")
for i in header_str.split("\r\n"):
if str(i).startswith("Sec-WebSocket-Key"):
header_dict["Sec-WebSocket-Key"] = i.split(":")[1].strip()
return header_dict
def get_header(data):
"""
将请求头格式化成字典
:param data:
:return:
"""
header_dict = {}
data = str(data, encoding='utf-8')
header, body = data.split('\r\n\r\n', 1)
header_list = header.split('\r\n')
for i in range(0, len(header_list)):
if i == 0:
if len(header_list[i].split(' ')) == 3:
header_dict['method'], header_dict['url'], header_dict['protocol'] = header_list[i].split(' ')
else:
k, v = header_list[i].split(':', 1)
header_dict[k] = v.strip()
return header_dict
headers = get_headers(data) # 提取请求头信息
# 对请求头中的sec-websocket-key进行加密
response_tpl = "HTTP/1.1 101 Switching Protocols\r\n" \
"Upgrade:websocket\r\n" \
"Connection: Upgrade\r\n" \
"Sec-WebSocket-Accept: %s\r\n" \
"WebSocket-Location: ws://127.0.0.1:9527\r\n\r\n"
value = headers['Sec-WebSocket-Key'] + magic_string
print(value)
ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())
response_str = response_tpl % (ac.decode('utf-8'))
# 响应【握手】信息
conn.send(response_str.encode("utf8"))
while True:
msg = conn.recv(8096)
print(msg)
#解密
# b'\x81\x83\xceH\xb6\x85\xffz\x85'
hashstr = b'\x81\x83\xceH\xb6\x85\xffz\x85'
# b'\x81 \x83 \xceH\xb6\x85\xffz\x85'
# 将第二个字节也就是 \x83 第9-16位 进行与127进行位运算
payload = hashstr[1] & 127
print(payload)
if payload == 127:
extend_payload_len = hashstr[2:10]
mask = hashstr[10:14]
decoded = hashstr[14:]
# 当位运算结果等于127时,则第3-10个字节为数据长度
# 第11-14字节为mask 解密所需字符串
# 则数据为第15字节至结尾
if payload == 126:
extend_payload_len = hashstr[2:4]
mask = hashstr[4:8]
decoded = hashstr[8:]
# 当位运算结果等于126时,则第3-4个字节为数据长度
# 第5-8字节为mask 解密所需字符串
# 则数据为第9字节至结尾
if payload <= 125:
extend_payload_len = None
mask = hashstr[2:6]
decoded = hashstr[6:]
# 当位运算结果小于等于125时,则这个数字就是数据的长度
# 第3-6字节为mask 解密所需字符串
# 则数据为第7字节至结尾
str_byte = bytearray()
for i in range(len(decoded)):
byte = decoded[i] ^ mask[i % 4]
str_byte.append(byte)
print(str_byte.decode("utf8"))
#加密
import struct
msg_bytes = "hello".encode("utf8")
token = b"\x81"
length = len(msg_bytes)
if length < 126:
token += struct.pack("B", length)
elif length == 126:
token += struct.pack("!BH", 126, length)
else:
token += struct.pack("!BQ", 127, length)
msg = token + msg_bytes
print(msg)
5. day05
1.Flask蓝图结构
#Flask蓝图结构
.
├── app #python项目目录-同步上传目录
│ ├── app #所有的app目录
│ │ ├── app01 #第一个应用
│ │ │ └── views.py #第一个应用的视图
│ │ ├── app02 #第二个应用
│ │ │ └── views.py #第二个应用的视图
│ │ └── main #主应用?默认应用?随便叫什么吧,就是首页,区别一下名字
│ │ └── views.py #该应用的视图
│ ├── build_requirements.py #生成requirements的程序
│ ├── main.py #主入口
│ └── requirements.txt #依赖包列表
├── dockerfile #生成docker镜像的dockerfile
└── rebuild.sh #自动停止容器,删除容器,重建镜像,启动镜像的linux shell脚本
2.SQLAlchemy
1.通过SQLAlchemy创建单表
#SQLAlchemy - ORM
#Object Relation Mapping
#python class - SQL
#Django - model - ORM
#Flask - SQLAlchemy
#通过SQLAlchemy创建单表
#1.导入SQLAlchemy
pip install SQLAlchemy
from sqlalchemy.ext.declarative import declarative_base
#2.创建ORM模型基类
Base=declarative_base()#Django Model
#3.导入ORM对应数据库数据类型的字段
from sqlalchemy import Column,Integer,String
#4.创建ORM对象
class User(Base):
__tablename__="user"
id=Column(Integer,primary_key=True,autoincrement=True)
name=Colume(String(32),index=True)
#5.创建数据库连接
from sqlalchemy import create_engine
engine=create_engine("mysql+pymysql://root:passwd@ip:port/dbname?charset=utf8")
#数据库连接创建完成
#6.去数据库中创建于User所对应的数据库表
#去engine数据库中创建所有继承Base类的ORM对象
Base.metadata.create_all(engine)
2.通过SQLAlchemy对数据表进行增删改查
#通过SQLAlchemy对数据表进行增删改查
#增加数据 insert
#insert into user(name) values("username")
#操作数据库 要打开数据库连接
from ct import engine
#创建会话 - 打开数据库连接
from sqlalchemy.orm import sessionmaker
#创建会话窗口
Session=sessionmaker(engine)
#打开会话窗口
db_session=Session()
from ct import User
user_obj=User(name="abc")
#通过db_session已经打开的会话窗口提交数据
db_session.add(user_obj)
#执行会话窗口中的所有操作
db_session.commit()
db_session.close()
#增加批量数据
from ct import User
db_session.add_all([
User(name="abc"),
User(name="def"),
User(name="zxc")
])
db_session.commit()
db_session.close()
#查询
#会话窗口
from sqlalchemy.orm import sessionmaker
from ct import engine
Session=sessionmaker(engine)
db_session=Session()
#简单查询
#select * from table
from ct import User
user_list=db_session.query(User).all()
for row in user_list:
print(row.id,row.name)
#user=db_session.query(User).first()
#print(user.id,user.name)
#带条件的查询
user_list=db_session.query(User).filter(User.id==4).all()
user=db_session.query(User).filter_by(id=4).first()
#查看sql语句
sql=db_session.query(User).filter(User.id>=4)
print()sql
#更新修改数据
#update user set name="abc" where id=1
from sqlalchemy.orm import sessionmaker
from ct import User
Session=sessionmaker(engine)
db_session=Session()
#修改数据
res=db_session.query(User).filter(User.name="abc").update({"name":"def"})
db_session.commit()
db_session.close()
#删除数据
#delete from user where id=1
from sqlalchemy.orm import sessionmaker
from ct import engine
Session=sessionmaker(engine)
db_session=Session()
#删除
res=db_session.query(User).filter(User.id==5).delete()
db_session.commit()
db_session.close()
#删除多条
res=db_session.query(User).filter(User.name=="123456").delete()
3.SQLAlchemy ForeignKey
1.一对多建表操作
#ForeignKey
#一对多建表操作
from sqlalchemy.ext.declarative import declarative_base
Base=declarative_base()
from sqlalchemy import Column,Integer,String,ForeignKey
from sqlalchemy.orm import relationship
class Student(Base):
__tablename__="student"
id=Column(Integer,primary_key=True)
name=Colume(String(32))
school_id=Colume(Integer,ForeignKey("school.id"))
#创建关系 relationship
stu2sch=relationship("School",backref="sch2stu")
class School(Base):
__tablename__="school"
id=Column(Integer,primary_key=True)
name=Colume(String(32))
from sqlalchemy import create_engine
engine=create_engine("mysql+pymysql://root:passwd@ip:port/dbname?charset=utf8")
Base.metadata.create_all(engine)
2.增删改查操作
#添加数据
from sqlalchemy.orm import sessionmaker
from ctf import engine,Student,School
Session=sessionmaker(engine)
db_session=Session()
#sch_obj=School(name="educt")
#db_session.add(sch_obj)
#db_session.commit()
#sch=db_session.query(School).filter(School.name="educt").first()
#stu_obj=Student(name="dec",school_id=sch.id)
#db_session.add(stu_obj)
#db_session.commit()
#db_session.close()
#添加数据 - 正向 relationship
stu_obj=Student(name="dec",stu2sch=School(name="educt"))
db_session.add(stu_obj)
db_session.commit()
db_session.close()
#添加数据 - 反向 relationship
sch_obj=School(name="educt")
sch_obj.sch2stu=[Student(name="dec"),Student(name="abc")]
db_session.add(sch_obj)
db_session.commit()
db_session.close()
#查询数据
from sqlalchemy.orm import sessionmaker
from ctf import engine,Student,School
Session=sessionmaker(engine)
db_session=Session()
#查询数据 - 正向 relationship
stu=db_session.query(Student).all()
for row in stu:
print(row.id,row.name,row.school_id,row.stu2sch.name)
#查询数据 - 反向 relationship
sch=db_session.query(School).all()
for school in sch:
for student in school.sch2stu:
print(school.id,school.name,student.name)
#修改数据
from sqlalchemy.orm import sessionmaker
from ctf import engine,Student,School
Session=sessionmaker(engine)
db_session=Session()
sch=db_session.query(School).filter(School.name=="educt").first()
db_session.query(Student).filter(Student.name=="dec").update({"school_id":sch.id})
db_session.commit()
db_session.close()
#删除数据
from sqlalchemy.orm import sessionmaker
from ctf import engine,Student,School
Session=sessionmaker(engine)
db_session=Session()
sch=db_session.query(School).filter(School.name=="educt").first()
db_session.query(Student).filter(Student.name==sch.id).delete()
db_session.commit()
db_session.close()
3.复杂的查操作
#查询操作
from my_create_table import User,engine
from sqlalchemy.orm import sessionmaker
Session = sessionmaker(engine)
db_session = Session()
# 查询数据表操作
# and or
from sqlalchemy.sql import and_ , or_
ret = db_session.query(User).filter(and_(User.id > 3, User.name == 'DragonFire')).all()
ret = db_session.query(User).filter(or_(User.id < 2, User.name == 'DragonFire')).all()
# 查询所有数据
r1 = db_session.query(User).all()
# 查询数据 指定查询数据列 加入别名
r2 = db_session.query(User.name.label('username'), User.id).first()
print(r2.id,r2.username) # 15 NBDragon
# 表达式筛选条件
r3 = db_session.query(User).filter(User.name == "DragonFire").all()
# 原生SQL筛选条件
r4 = db_session.query(User).filter_by(name='DragonFire').all()
r5 = db_session.query(User).filter_by(name='DragonFire').first()
# 字符串匹配方式筛选条件 并使用 order_by进行排序
r6 = db_session.query(User).filter(text("id<:value and name=:name")).params(value=224, name='DragonFire').order_by(User.id).all()
#原生SQL查询
r7 = db_session.query(User).from_statement(text("SELECT * FROM User where name=:name")).params(name='DragonFire').all()
# 筛选查询列
# query的时候我们不在使用User ORM对象,而是使用User.name来对内容进行选取
user_list = db_session.query(User.name).all()
print(user_list)
for row in user_list:
print(row.name)
# 别名映射 name as nick
user_list = db_session.query(User.name.label("nick")).all()
print(user_list)
for row in user_list:
print(row.nick) # 这里要写别名了
# 筛选条件格式
user_list = db_session.query(User).filter(User.name == "DragonFire").all()
user_list = db_session.query(User).filter(User.name == "DragonFire").first()
user_list = db_session.query(User).filter_by(name="DragonFire").first()
for row in user_list:
print(row.nick)
# 复杂查询
from sqlalchemy.sql import text
user_list = db_session.query(User).filter(text("id<:value and name=:name")).params(value=3,name="DragonFire")
# 查询语句
from sqlalchemy.sql import text
user_list = db_session.query(User).filter(text("select * from User id<:value and name=:name")).params(value=3,name="DragonFire")
# 排序 :
user_list = db_session.query(User).order_by(User.id).all()
user_list = db_session.query(User).order_by(User.id.desc()).all()
for row in user_list:
print(row.name,row.id)
#其他查询条件
ret = session.query(User).filter_by(name='DragonFire').all()
ret = session.query(User).filter(User.id > 1, User.name == 'DragonFire').all()
ret = session.query(User).filter(User.id.between(1, 3), User.name == 'DragonFire').all() # between 大于1小于3的
ret = session.query(User).filter(User.id.in_([1,3,4])).all() # in_([1,3,4]) 只查询id等于1,3,4的
ret = session.query(User).filter(~User.id.in_([1,3,4])).all() # ~xxxx.in_([1,3,4]) 查询不等于1,3,4的
ret = session.query(User).filter(User.id.in_(session.query(User.id).filter_by(name='DragonFire'))).all() 子查询
from sqlalchemy import and_, or_
ret = session.query(User).filter(and_(User.id > 3, User.name == 'DragonFire')).all()
ret = session.query(User).filter(or_(User.id < 2, User.name == 'DragonFire')).all()
ret = session.query(User).filter(
or_(
User.id < 2,
and_(User.name == 'eric', User.id > 3),
User.extra != ""
)).all()
# select * from User where id<2 or (name="eric" and id>3) or extra != ""
# 通配符
ret = db_session.query(User).filter(User.name.like('e%')).all()
ret = db_session.query(User).filter(~User.name.like('e%')).all()
# 限制
ret = db_session.query(User)[1:2]
# 排序
ret = db_session.query(User).order_by(User.name.desc()).all()
ret = db_session.query(User).order_by(User.name.desc(), User.id.asc()).all()
# 分组
from sqlalchemy.sql import func
ret = db_session.query(User).group_by(User.extra).all()
ret = db_session.query(
func.max(User.id),
func.sum(User.id),
func.min(User.id)).group_by(User.name).all()
ret = db_session.query(
func.max(User.id),
func.sum(User.id),
func.min(User.id)).group_by(User.name).having(func.min(User.id) >2).all()
# 关闭连接
db_session.close()
4.复杂的改操作
#修改操作
from my_create_table import User,engine
from sqlalchemy.orm import sessionmaker
Session = sessionmaker(engine)
db_session = Session()
#直接修改
db_session.query(User).filter(User.id > 0).update({"name" : "099"})
#在原有值基础上添加 - 1
db_session.query(User).filter(User.id > 0).update({User.name: User.name + "099"}, synchronize_session=False)
#在原有值基础上添加 - 2
db_session.query(User).filter(User.id > 0).update({"age": User.age + 1}, synchronize_session="evaluate")
db_session.commit()
4.SQLAlchemy M2M
1.多对多建表操作
#SQLAlchemy M2M 多对多 relationship
from sqlalchemy.ext.declarative import declarative_base
Base=declarative_base()
from sqlalchemy import Column,Integer,String,ForeignKey
from sqlalchemy.orm import relationship
class Girls(Base):
__tablename__="girl"
id=Column(Integer,primary_key=True)
name=Column(String(32))
#创建关系
girl2boy=relationship("Boys",secondray="hotel",backref="boy2girl")
class Boys(Base):
__tablename__="boy"
id=Column(Integer,primary_key=True)
name=Column(String(32))
class Hotel(Base):
__tablename__="hotel"
id=Column(Integer,primary_key=True)
girl_id=Column(Integer,ForeignKey("girl.id"))
boy_id=Column(Integer,ForeignKey("boy.id"))
from sqlalchemy import create_engine
engine=create_engine("mysql+pymysql://root:passwd@ip:port/dbname?charset=utf8")
Base.metadata.create_all(engine)
2.增查操作
#添加数据
from sqlalchemy.orm import sessionmaker
from s5m2m import engine,Boys,Girls
Session=sessionmaker(engine)
db_session=Session()
#通过Boy添加Girl和Hotel数据 relationship 反向
boy=Boys(name="dec")
boy.boy2girl=[Girls(name="abc"),Girls(name="def")]
db_session.add(boy)
db_session.commit()
db_session.close()
#通过Girl添加Boy和Hotel数据 relationship 正向
Girls(name="aaa",girl2boy=[Boys(name="dec"),Boys(name="ch")])
db_session.add(girl_obj)
db_session.commit()
#查询数据 relationship 正向
g_list=db_session.query(Girls).all()
for girl in g_list:
for boy in girl.girl2boy:
print(girl.name,boy)
#查询 relationship 反向
b_list=db_session.query(Boys).all()
for boy in b_list:
for girl in boy.boy2girl:
print(boy.name,girl.name)
5.Flask-SQLAlchemy
pip install Flask-SQLAlchemy
1.加入Flask-SQLAlchemy第三方组件
#MyApp/__init__.py
from flask import Flask
# 导入Flask-SQLAlchemy中的SQLAlchemy
from flask_sqlalchemy import SQLAlchemy
# 实例化SQLAlchemy
db = SQLAlchemy()
# PS : 实例化SQLAlchemy的代码必须要在引入蓝图之前
from .views.users import user
def create_app():
app = Flask(__name__)
# 初始化App配置 这个app配置就厉害了,专门针对 SQLAlchemy 进行配置
# SQLALCHEMY_DATABASE_URI 配置 SQLAlchemy 的链接字符串儿
app.config["SQLALCHEMY_DATABASE_URI"] = "mysql+pymysql://root:123456@127.0.0.1:3306/db_name?charset=utf8"
# SQLALCHEMY_POOL_SIZE 配置 SQLAlchemy 的连接池大小
app.config["SQLALCHEMY_POOL_SIZE"] = 5
# SQLALCHEMY_POOL_TIMEOUT 配置 SQLAlchemy 的连接超时时间
app.config["SQLALCHEMY_POOL_TIMEOUT"] = 15
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
# 初始化SQLAlchemy , 本质就是将以上的配置读取出来
db.init_app(app)
app.register_blueprint(user)
return app
2.建立models.py ORM模型文件
#MyApp/models.py
from MyApp import db
Base = db.Model # 这句话你是否还记的?
# from sqlalchemy.ext.declarative import declarative_base
# Base = declarative_base()
# 每一次我们在创建数据表的时候都要做这样一件事
# 然而Flask-SQLAlchemy已经为我们把 Base 封装好了
# 建立User数据表
class Users(Base): # Base实际上就是 db.Model
__tablename__ = "users"
__table_args__ = {"useexisting": True}
# 在SQLAlchemy 中我们是导入了Column和数据类型 Integer 在这里
# 就和db.Model一样,已经封装好了
id = db.Column(db.Integer,primary_key=True)
username = db.Column(db.String(32))
password = db.Column(db.String(32))
if __name__ == '__main__':
from MyApp import create_app
app = create_app()
# 这里你要回顾一下Flask应该上下文管理了
# 离线脚本:
with app.app_context():
db.drop_all()
db.create_all()
3.登录视图函数的应用
#MyApp/views/user.py
from flask import Blueprint, request, render_template
user = Blueprint("user", __name__)
from MyApp.models import Users
from MyApp import db
@user.route("/login",methods=["POST","GET"])
def user_login():
if request.method == "POST":
username = request.form.get("username")
password = request.form.get("password")
# 还记不记得我们的
# from sqlalchemy.orm import sessionmaker
# Session = sessionmaker(engine)
# db_sesson = Session()
# 现在不用了,因为 Flask-SQLAlchemy 也已经为我们做好会话打开的工作
db.session.add(Users(username=username,password=password))
db.session.commit()
# 然后再查询
user_info = Users.query.filter(Users.username == username and User.password == password).first()
print(user_info.username)
if user_info:
return f"登录成功{user_info.username}"
return render_template("login.html")
6.Flask-Script
pip install Flask-Script
1.将 Flask-Script 加入到 Flask 项目中
#MyApp/manager.py
import MyApp
# 导入 Flask-Script 中的 Manager
from flask_script import Manager
app = MyApp.create_app()
# 让app支持 Manager
manager = Manager(app)
if __name__ == '__main__':
#app.run()
# 替换原有的app.run(),然后大功告成了
manager.run()
2.使用命令启动 Flask 项目
python manager.py runserver
#启动项目并更改配置参数(监听IP地址,监听端口)
python manager.py runserver -h 0.0.0.0 -p 9527
3.高级操作 - 自定制脚本命令
方式一 : @manager.command
#MyApp/manager.py
import MyApp
# 导入 Flask-Script 中的 Manager
from flask_script import Manager
app = MyApp.create_app()
# 让app支持 Manager
manager = Manager(app) # type:Manager
@manager.command
def prt(arg):
print(arg)
if __name__ == '__main__':
#app.run()
# 替换原有的app.run(),然后大功告成了
manager.run()
python manager.py prt 666
方式二 : @manager.opation("-短指令","--长指令",dest="变量名")
#MyApp/manager.py
import MyApp
# 导入 Flask-Script 中的 Manager
from flask_script import Manager
app = MyApp.create_app()
# 让app支持 Manager
manager = Manager(app) # type:Manager
@manager.command
def DragonFire(arg):
print(arg)
@manager.option("-n","--name",dest="name")
@manager.option("-s","--say",dest="say")
def talk(name,say):
print(f"{name}你可真{say}")
if __name__ == '__main__':
#app.run()
# 替换原有的app.run(),然后大功告成了
manager.run()
python manager.py talk -n dec -s dc
python manager.py talk --name dec --say dc
7.Flask-Migrate
pip install Flask-Migrate
1.将 Flask-Migrate 加入到 Flask 项目中
PS: 注意了 Flask-Migrate 是要依赖 Flask-Script 组件的
#MyApp/manager.py
import MyApp
# 导入 Flask-Script 中的 Manager
from flask_script import Manager
# 导入 Flask-Migrate 中的 Migrate 和 MigrateCommand
# 这两个东西说白了就是想在 Flask-Script 中添加几个命令和指令而已
from flask_migrate import Migrate,MigrateCommand
app = MyApp.create_app()
# 让app支持 Manager
manager = Manager(app) # type:Manager
# Migrate 既然是数据库迁移,那么就得告诉他数据库在哪里
# 并且告诉他要支持那个app
Migrate(app,MyApp.db)
# 现在就要告诉manager 有新的指令了,这个新指令在MigrateCommand 中存着呢
manager.add_command("db",MigrateCommand) # 当你的命令中出现 db 指令,则去MigrateCommand中寻找对应关系
"""
数据库迁移指令:
python manager.py db init
python manager.py db migrate # Django中的 makemigration
python manager.py db upgrade # Django中的 migrate
"""
if __name__ == '__main__':
#app.run()
# 替换原有的app.run(),然后大功告成了
manager.run()
2.执行数据库初始化指令
数据库迁移指令:
python manager.py db init
python manager.py db migrate # Django中的 makemigration
python manager.py db upgrade # Django中的 migrate

浙公网安备 33010602011771号