Flask 轻便的 web框架-3
简述概要
1.flask启动
安装:pip install flask
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return
2.flask response
'' #返回字符串,相当于HTTPResponse
render_template('index.html')默认存放路径templates #返回模版,依赖MarkupSafe
redirect('/login') #响应头中加入:location:http://www.baidu.com 重定向302
app.run("0.0.0.0",8888) #监听地址,监听端口
Flask的特殊Response
send_file(文件名称或文件路经+文件名称)#打开并返回文件内容,自动识别文件类型 响应头 Content-Type:文件类型
jsonify({"k1":"v1"}) #返回标准格式的JSON字符串 响应头中Content-Type
#Flask 1.1.1中直接返回dict 本质上是在运行jsonify
3.flask request
from flask import request
request.form 获取formData中的数据 to_dict() 类似字典类型.获取方式:.get() ["key"](有错会报KeyError)
request.args 获取URL中的数据 to_dict() 类似字典类型, .get() ["key"]()(有错会报KeyError)
request.json 请求头中 Content-Type:application/json 存放字典
request.data 请求头中Content-Type 没有Form FormData 存放b""
request.method 请求方式
request.cookies 浏览器的cookie键值对
request.headers 请求头
request.path 路由地址,请求地址
request.url 请求全部地址
request.host 主机位
4.flask session
交由客户端保管的一串加密字符串
在服务器存储的键值对
from flask import session
app.config["SECRET_KEY"] = "!ddsf#$@##()&$@*$@!~~ds"
session["key"] = "value"
5.flask 路由
@app.route(
rule,#路由地址
endpoint,#Mapping名称对应路由地址 -->通过url_for 反向推推路由地址url_for(endpoint)
methods,#允许进入视图函数的请求方式
defaults,#默认参数
redirect_to,#永久重定向 301 08
strict_slashes,#是否严格遵循路由匹配规则 /
)
app.add_url_rule(rule,
view_func #视图函数
)
动态参数路由:
/get_file/<filename>
def get_file(filename):
...
6.flask 初始化配置
app = Flask(__name__)
template_folder 模板存放目录
static_folder 静态文件存放目录
static_url_path 静态文件访问路径
7.flask config
class DebugConfig(object):
DEBUG = True
app = Flask(__name__)
app.config.from_object(DebugConfig)
8.flask 蓝图(隔离app)
from flask import Blueprint
#当成不能被run的Flask实例
bp = Blueprint("bp",__name__,url_prefix)
#url_prefix 为url前缀
@bp.route("/mybp",endpoint='666')
bp.add_url_rule()
url_for("蓝图标识.endpoint")#url_for("bp.666")
app.register_blueprint()
9.flask 特殊装饰器
@app.before_request 在请求进入视图函数之前,做出处理
@app.after_request 在结束视图函数之后,响应客户端之前
@app.errorhandler(HTTP错误码) 重定义错误页面
10.flask CBV
from flask import views
class Login(views.MethodView):
def get(self,*args,**kwargs):
...
def post(self,*args,**kwargs):
...
app.add_url_rule("/login",endpoint=None,view_func=Login.as_view(name="阿斯达多"))
- 示例:CBV创建一个登陆效果
from flask import views,Flask,render_template,request
from markupsafe import Markup
app = Flask(__name__,template_folder="templates")
class Login(views.MethodView):#继承当前MethodView,让我当前class,可以成为视图类
methods=["GET","POST"]#methods方法要不不写,要不写全。默认写了几个方法,就有几个方法
def get(self,*args,**kwargs):
return render_template("login.html")
def post(self,*args,**kwargs):
userinfo = request.form.to_dict()
user = userinfo.get("user",'')
pwd = userinfo.get("pwd","")
if user == "root" and pwd == "123":
return Markup("<h1>welcome to home</h1>")
return Markup("<h1>密码账号错误</h1>")
app.add_url_rule("/login",endpoint=None,view_func=Login.as_view(name="login"))
#name必须填写否则报错。
if __name__ == '__main__':
app.run()
-
源码实现:
-
add_url_rule
自定义类CBV,需要继承MethodView,而MethodView继承View和MethodViewType
-
as_view()
- 执行as_view()方法,因MethodView没有as_view方法,继承View的as_view,在执行初始化类时,MethodView和View均没有初始化,MethodView通过继承MethodViewType找到
__init__
- 执行as_view()方法,因MethodView没有as_view方法,继承View的as_view,在执行初始化类时,MethodView和View均没有初始化,MethodView通过继承MethodViewType找到
-
自己有dispatch_request,父类也有dispatch_request,先执行自己的dispatch_request
- 类中添加装饰器
def is_login(func):
def inner(*args,**kwargs):
start_time = time.time()
ret = func(*args,**kwargs)
end_time = time.time()
print(end_time-start_time)
return ret
return inner
class Login(views.MethodView):
decorators = [is_login,]#在类中添加装饰器,其实没什么卵用
def get(self,*args,**kwargs):
...
def post(self,*args,**kwargs):
...
11.flask 上下文管理
- 前奏,关于flask上下文管理,需要先来说下两个知识点:
1.偏函数
from functools import partial
#demo1:
def add_num(a,b):
return a + b
print(add_num(10,20))#此时打印30
#demo2:
new_func1 = partial(add_num,10)
print(new_func1(5))#15
#demo3:
new_func2 = partial(add_num,5,5)
print(new_func2())#10
#通过上面3个例子,由上面代码知道一些规律了。第一个例子是一个普通函数,第二个例子在函数add_num调用时,我们已经知道了其中一个参数,并且通过这个参数绑定一个新函数,也就是new_func1 = partial(add_num,10),通过调用new_func1,并传入另外一个参数,就可以执行。
#第三个例子与第二个例子相似,只不过新函数new_func2已经将所有参数传入,只要加括号就可以执行函数
2.线程的安全
import threading
from threading import Thread
import time
from threading import local
#在内存中重新开辟空间,copy原来值。
# rom = {
# 9527:"foo.num = 0",
# 3721:"foo.num = 1",
# 7394:"foo.num = 2",
# ...
# }
class Foo(local):
num = 0
foo = Foo()
def addi(i,):
foo.num = i
time.sleep(0.2)#相当于io操作
print(foo.num,threading.currentThread().ident)#打印数值,打印线程id
for i in range(20):
t = Thread(target=addi,args=(i,))
t.start()
- 创建20个线程,操作foo.num,上述代码中foo.num成为为了公共资源,每个子线到time.sleep(0.2)阻塞,每个子线程互相干扰,在阻塞前,所有子线程创建完毕,最后 i 的值为19,所有子线程打印foo.num打印值都为19,最终导致各自子线程输入的值不是自己所期望的值,如果进行加锁,虽然数据安全了,开多线程意义也不大了。通过导入模块local类,并由Foo继承,结果打印结果1,0,2,4....19之间打印结果不重复。
- local类实现机理:其实每个子线程在内存中开辟一个空间,来储存当前线程所用的值(foo.num的值,线程独享),浪费了空间,节约了时间。
- 不同线程使用这个对象存储的数据其它线程不可见(本质上就是不同的线程使用这个对象时为其创建一个独立的字典)。
3.flask上文管理(***************)
-
app = Flask(
__name__
) -
客户端的请求进来时会调用
app.__call__
中app.wsgi_app()
- 把environ 里面数据变得和flask的request一样(下面三张图里面类主要是把environ 里面数据变得和flask的request一样):
-
返回到request_context
-
继续返回
- get_ident为导入的变量,为了是获取当前线程/协程的id
- 回到global.py
- 此时会发生错误
- 如果获取stack失败后,会返回None
- 回到ctx.py ,此时top为None
4.flask下文管理(***************)
request全局对象
- 进入_local --->Local()----->执行
__getattr__
方法
- 此时top = ctx-request/session
- 执行return getattr(top, name) 此时name="request"
- 将top里的request真身对象取出,然后返回给LocalProxy(global.py里)
request = LocalProxy(partial(_lookup_req_object, "request"))
#虽然响应结果是request,但是并没有执行,只是一个新的函数,还没有执行。
- 那么在哪里request获得最终我们想要的request对象。通过LocalProxy的实例化:
- 当执行request.method方法LocalProxy的实例好的对象执行
__getattr__
方法
def __getattr__(self, name):
if name == "__members__":#此时name = "method"
return dir(self._get_current_object())
return getattr(self._get_current_object(), name)#执行_get_current_object
def _get_current_object(self):
if not hasattr(self.__local, "__release_local__"):
#没有"__release_local__"方法,
return self.__local()#返回一个执行方法,此时self.__local 等于newfunc的request对象,执行获得request真身对象
try:
return getattr(self.__local, self.__name__)
except AttributeError:
raise RuntimeError("no object bound to %s" % self.__name__)
????
- 这样request.method就很容易拿到了
12.Redis简单指令
-
set key value 用来在数据库中设置一个键值对,哈希存储结构
-
get key 返回value 用来从数据库取出key的响应value
-
keys(查询key值) 例如 keys * 查询当前数据中所有的key
keys a* 查询当前数据库中所有 以a开头的key
-
select dbnum : 总共16个库。数据库切换指定,数据隔离
13.Flask 第三方组件--->Flask-Session
-
flask-session是flask的session组件,由于原来flask内置session使用签名cookie保存,该组件将支持session保存到多个地方如:
- redis
- memcached
- filesystem
- mongodb
- sqlalchmey:拿数据存到数据库表里面
-
flask-session下载:
pip install flask-session
-
flask-session配置:
app.config['SESSION_TYPE'] = 'redis' # session类型为redis app.config['SESSION_PERMANENT'] = False # 如果设置为True,则关闭浏览器session就失效。 app.config['SESSION_USE_SIGNER'] = False # 是否对发送到浏览器上session的cookie值进行加密 app.config['SESSION_KEY_PREFIX'] = 'session:' # 保存到session中的值的前缀 app.config["SESSION_REDIS"] = Redis(host="127.0.0.1",port=6379,db=0) # 用于连接redis的配置
-
也可以在settings配置
from redis import Redis class DebugConfig(object): DEBUG = True PERMANENT_COOKIE_NAME = 3600 #浏览器保存的session名字: SESSION_COOKIE_NAME = "I AM DEBUG SESSION" SESSION_TYPE = "redis" SESSION_REDIS = Redis(host="127.0.0.1", port=6379, db=0) SESSION_KEY_PREFIX = 'my_session' SESSION_USE_SIGNER = False SESSION_PERMANENT = False
-
session存入redis
from flask import Flask,session from flask_session import Session from settings import DebugConfig app = Flask(__name__) app.config.from_object(DebugConfig) Session(app) @app.route("/set_s") def set_session(): """创建一个session""" session["key"] = "i am session" return "Set Success!" @app.route("/get_s") def get_session(): """获取一个session""" ses = session.get("key","None") return ses if __name__ == '__main__': app.run()
通过:self._get_interface 改写配置
app的配置才决定第三方组件的对接
3.SQLAlchemy学习
-
ORM中数据表是什么?
Object Relation Mapping 对象关系映射
-
创建一个Object
class User(object): pass
-
下载SQLAlchemy
pip install SQLAlchemy 需要下载pymysql
3.1创建数据表:
#my_create_table.py
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column,Integer,String
Base = declarative_base()#实例化官宣模型 Base就是ORM模型
class User(Base):#继承Base,相当于Django Models中Model
__tablename__ = "user"#创建表名为user
"""
id = Column(数据类型,索引,主键,外键,等等)
"""
id = Column(Integer,primary_key=True,autoincrement=True)
name = Column(String(32),index=True)#str相当于sql里的char
#在数据库中创建数据表 或 连接数据库
from sqlalchemy import create_engine
#创建数据库引擎
engine = create_engine("mysql+pymysql://root:123@127.0.0.1:3306/sqlarm?charset=utf8")
#自动检索所有继承Base的ORM对象,并且创建所有数据表
Base.metadata.create_all(engine)
3.2增加数据
#!/usr/bin/env python
# -*- coding:utf-8 -*-
"""
Xu Junkai
"""
from my_create_table import User#导入之前做好的ORM表
user1 = User(name="xujunkai")#使用User ORM模型创建一条数据
#写入数据库,首先打开数据库会话,直白就是创建一个操作数据库的窗口
from sqlalchemy.orm import sessionmaker
#导入之前创建好的create_engine
from my_create_table import engine
#创建sessionmaker会话对象,将数据库引擎engine交给sessionmaker
Session = sessionmaker(engine)
#打开会话对象Session
db_session = Session()
#使用db_session会话提交,将db_session中所有指令一次性提交
# db_session.add(user1)
# db_session.commit()
#增加多个数据
user_list = [
User(name = "金城武"),
User(name = "刘德华"),
User(name = "梁朝伟"),
]
db_session.add_all(user_list)
db_session.commit()
3.3查询数据
- 查询表中所有数据
from my_create_table import User,engine
from sqlalchemy.orm import sessionmaker
Session = sessionmaker(engine)
db_session = Session()
# 1. select * from user 查询user表中的所有数据
# 语法是这样的 使用 db_session 会话 执行User表 query(User) 取出全部数据 all()
user_all_list = db_session.query(User).all()
print(user_all_list)#[<my_create_table.User object at 0x0000022C81769B00>,...]
for item in user_all_list:
print(item.id,item.name)# ORM对象 直接使用调用属性的方法 拿出对应字段的值
db_session.close()
- 条件查询:
#查询id>=2的数据
user = db_session.query(User).filter(User.id >=2)
print(user)#SELECT user.id AS user_id, user.name AS user_name #显示原生sql
for item in user:
print(item.id,item.name)
3.4修改数据
from my_create_table import User,engine
from sqlalchemy.orm import sessionmaker
Session = sessionmaker(engine)
db_session = Session()
# UPDATE user SET name="NBDragon" WHERE id=2 更新一条数据
#将id=2的用户名更改为古天乐
res = db_session.query(User).filter(User.id == 2).update({"name":"古天乐"})
print(res) #1 res就是我们当前这句更新语句所更新的行数
db_session.commit()
db_session.close()
- 更新多条数据
#将id小于等于4的name改为xjk
res = db_session.query(User).filter(User.id <=4).update({"name":"xjk"})
print(res)
3.5删除数据
- 删除单条数据
#导入 ORM 创建会话
from my_create_table import User,engine
from sqlalchemy.orm import sessionmaker
Session = sessionmaker(engine)
db_session = Session()
#DELETE FROM `user` WHERE id=2;
res = db_session.query(User).filter(User.id==2).delete()
print(res)
db_session.commit()
db_session.close()
- 删除多条数据
#DELETE FROM `user` WHERE id>2;
res = db_session.query(User).filter(User.id>2).delete()
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库