zoukankan      html  css  js  c++  java
  • 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()
    
  • 相关阅读:
    Samba 4.0 RC3 发布
    SymmetricDS 3.1.7 发布,数据同步和复制
    Express.js 3.0 发布,Node.js 的高性能封装
    GIFLIB 5.0.1 发布,C语言的GIF处理库
    jQuery UI 1.9.1 发布
    SVN Access Manager 0.5.5.14 发布 SVN 管理工具
    DynamicReports 3.0.3 发布 Java 报表工具
    HttpComponents HttpClient 4.2.2 GA 发布
    AppCan 2.0 正式发布,推移动应用云服务
    Ruby 2.0 的新功能已经冻结
  • 原文地址:https://www.cnblogs.com/xujunkai/p/12349882.html
Copyright © 2011-2022 走看看