• 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()
    
  • 相关阅读:
    我和杨兄的不同的Code First简单权限设计
    JavaScript&JQ 004_JS闭包
    省市区三级联动[JSON+Jquery]
    JavaScript 005_JS数组的CRUD
    linq头脑风暴001_聚合函数
    类成员函数指针的特殊之处
    模拟手算,计算组合数C(m,n)
    命令行版扫雷(vc08)
    UNICODE,GBK,UTF8:编码格式的区别
    画高频PCB的心得
  • 原文地址:https://www.cnblogs.com/xujunkai/p/12349882.html
Copyright © 2020-2023  润新知