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__

  • 自己有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()
原文地址:https://www.cnblogs.com/xujunkai/p/12349882.html