• Flask总结


    1.配置文件

    2.before_request/after_request

    3.路由系统

    4.视图

    5.模板

    6.session

    7.flash

    8.蓝图(blueprint)

    flask-组件

    1.flask-session

    2.DBUtils

    3.wtforms

    4.SQLAlchemy

    5.flask-script

    6.flask-migrate

    偏函数

    上下文管理

    用flask写一个hello world文件

    from flask import Flask
    app=Flask(__name__)
    @app.route("/index")
    def index():
        return "Hello Word"
    if __name__=="__main__":
        app.run()

           配置文件    

    1.新建settings.py 存放配置文件
    class Config(object):
        DEBUG = False
        SECRET_KEY = "asdfasdfasdf"
    #上线环境
    class ProductionConfig(Config):
        DEBUG = False
    #开发环境
    class DevelopmentConfig(Config):
        DEBUG = True
    #测试环境
    class TestingConfig(Config):
        TESTING = True
    其他文件要引入配置文件中的内容 如:
     app.config.from_object('settings.DevelopmentConfig')
    引入整个py文件:app.config.from_pyfile("settings.py")

       before_request和after_request  

    before_request和after_request
    相当于Django中的中间件
    after_request的视图中必须有参数,且必须有返回值
    1.before_request---->目标请求 ----->after_request
    @app.before_request
    def a1():
        print("a2")
    @app.after_request
    def c1(response):
        print("c1")
        return response
    def index():
        print("index")
        return render_template("index.html")
    执行顺序:a2--->index---->c1
    2.在走before_request时如果返回了值,后面的before_request就不再执行,目标函数也不执行
    会把after_request都走一遍
    @app.before_request
    def a1():
        print("a1")
    @app.before_request   
    def d1(response):
        print("d1")
        return response
    @app.after_request
    def c1(response):
        print("c1")
        return response
        return response
    def index():
        print("index")
        return render_template("index.html")
    执行顺序: a1--->d1--->c1

        路由系统   

    路由的两种注册方法:
        1.使用装饰器
            @app.route("/login")
        2. app.add_url_rule(rule='/login', view_func=index)
    1.定义methods
        @app.route("/login",methods=['GET','POST'])
    2.动态路由
        URL: http://www.xx.com/index/1/88
        @app.route('/index/<string:nid>/<int:num>')
    也可以 @app.route('/index/<nid>/<num>')
          def index(a,b):pass
    3.endpoint ,为url起别名,根据别名可以反向生成URL(默认endpoint是函数名)
      url_for:反向解析
      url_for()它把函数名称作为第一个参数。它可以接受任意个关键字参数,每个关键字参数对应URL中的变量
      1.自定义endpoint
          @app.route('/user/<username>',endpoint="aaa")
        def profile(username):print(url_for('aaa',username='zhao')  /user/zhao
      2.使用默认的endpoint
        @app.route('/login')
        def login():pass
        @app.route('/user/<username>')
        def profile(username):pass
        with app.test():
            print(url_for('login')) /login
            print(url_for('login', next='/')) /login?next=/
            print(url_for('profile', username='zhao')) /user/zhao
    4.支持自定义正则
    @app.route('/index/<string:nid>/<int:num>/<regex("d+"):xxxx>')
    5.支持FBV和CBV
    from flask import Flask,url_for,views
    class IndexView(views.MethodView):
        methods = ['GET','POST']
        decorators = [auth, ]
        def get(self):
            return 'Index.GET'    
        def post(self):
            return 'Index.POST'
    app.add_url_rule('/index', view_func=IndexView.as_view(name='ci')) name="ci" 指别名
    View Code

             视图     

    from flask import request,render_template,redirect
    1.请求:
        request.method
        request.args
        request.form
        request.cookies
        request.headers
        request.path
        request.full_path
        request.url
        request.files
        obj = request.files['the_file_name']
    2.响应:
        1.return "xxxx" 返回字符串
        2.return render_template("index.html",msg="xxx") 返回模板
          return render_template('html模板路径',**{"mag="xxx”,arg":12})
        3.return redirect() 重定向
        4.response = make_response(render_template('index.html'))
        5. response.set_cookie('key', 'value')
        6. response.delete_cookie('key')
        7.return send_file()
        def get_img(file_name):
        file_path = os.path.join(setting.RESOURCE_IMG_PATH,file_name)
        return send_file(file_path)
    @app.route('/setco')
    def setco():
        from flask import make_response
        obj = make_response("返回内容...")
        obj.set_cookie('a1','123')
        return obj
    给视图添加装饰器:
            - 装饰器必须设置functools.wappers
            - 紧挨着放在视图之上
    
    from functools import wraps
    def is_login(func):
        @wraps(func) #保留函数元信息
        def inner(*args,**kwargs):
            user_info = session.get('user')
            if not user_info:
                return redirect('/login')
            ret=func()
            return ret
        return inner
    @app.route("/book")
    @is_login  #必须紧贴着函数
    def book():
        return "Book"
    View Code

             模板     

    Flask使用的是Jinja2模板,所以其语法和Django无差别
    1.支持python语法
    2.支持模板继承
    3.自定义函数

    服务端:
    def jerd():
        return '<h1>jerd</h1>'
    @app.route('/login', methods=['GET', 'POST'])
    def login():
        return render_template('login.html', ww=jerd)
    HTML:需要加()和safe
    <body>
        {{ww()|safe}}
    </body>
    4.全局函数
    1.global:
        @app.template_global()
        def test4(arg):
            return arg + 100
        所有视图中都可以调用:
            {{ test4(666) }}
    2.filter:
        @app.template_filter()
        def db(a1, a2, a3):
            return a1 + a2 + a3
        所有视图中都可以调用:
            {{ 1|db(2,3)}}

         Session   

    session的本质是字典
    flask的session存放在cookie中
    1.导入session
    from flask import session
    2.设置key值:app.secret_key="xxxxx"
    3.操作:
        1.设置:session["zhao"]="zgf"
        2.获取:session.get("zhao")
        3.删除:del session.["zhao"]
    在客户端的cookie中能看到session和sessionid
    
    cookie和session的区别?
        cookie,是保存在用户浏览器端的键值对,可以用来做用户认证。
        session,将用户会话信息保存在服务端{adfasdfasdf:"...",99dfsdfsdfsd:'xxx'},依赖cookie将每个用户的随机字符串保存到用户浏览器上;
        
        django,session默认保存在数据库;django_session表
        flask,session默认将加密的数据写用户cookie中。

          flash,闪现   

    基于session实现
    from flask import flash,get_flashed_messages
        @app.route('/set_flash')
        def set_flash():
            flash('666',category='error')
            flash('999',category='log')
            return "设置成功"
        
        @app.route('/get_flash')
        def get_flash():
            data = get_flashed_messages(category_filter=['error'])
            print(data)
            return "或成功"

        蓝图(blueprint)  

    app---蓝图----视图
    1. 目录结构的划分(解耦)。
    2. 单蓝图中应用before_request
    3. URL划分
        app.register_blueprint(account,url_prefix='/user')
    4. 反向生成URL
        url_for('蓝图.函数名')
        url_for('蓝图.endpoint')
        url_for('蓝图.endpoint',nid=1)
        print(url_for('account.login'))
    在app下注册蓝图
    app=Flask(__name__)
    app.register_blueprint(bpmanager)
    在视图中:
    from flask import Blueprint
    bpmanager=Blueprint('bpmanager',__name__)
    @bpmanager.route("/delete/",methods=["GET","POST"])
    def delete():pass

        flask-session   

    1.flask-session的作用:
    将flask中的session由加密存到cookie中的方式更换为放置到其他的数据源
    如:redis/memcached/filesystem/mongodb/sqlalchemy(数据库)
    2.简述flask-session的原理?
    1.请求进来走完before-request后走到open-session方法,该方法从cookie中读取session_id
    对应的随机字符串。
    2.如果未获取到这个字符串,就创建一个随机字符串并在内存中创建个特殊的字典
    如果能获取到这个字符串,就根据这个随机字符串去redis中获取原来设置的值,并在在内存创建字典
    3.在视图函数中,对内存中的字典进行操作
    4.当请求结束时,执行save_session方法,
    1.该方法去读取内存中特殊的字典,并将字典序列化成字符串
    2.将字符串写到redis中
    3.将随机字符串写到cookie中
    操作:
    1.安装:pip3 install flask-session
    2.在配置文件中:
        SESSION_TYPE='redis'
        SESSION_REDIS = Redis(host='127.0.0.1',port=6379)
    3.引入session
    from flask_session import Session
    app = Flask(__name__)
    Session(app)

       DBUtils 数据库连接池 

    1.pip3 install DBUtils 
    2.配置连接池:
    import pymysql
    from DBUtils.PooledDB import PooledDB, SharedDBConnection
    POOL = PooledDB(
        creator=pymysql,  # 使用链接数据库的模块
        maxconnections=20,  # 连接池允许的最大连接数,0和None表示不限制连接数
        mincached=2,  # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
        maxcached=5,  # 链接池中最多闲置的链接,0和None不限制
        maxshared=0,  # 链接池中最多共享的链接数量,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='day118',
        charset='utf8'
    )
    使用:
        conn = POOL.connection()
        cursor = conn.cursor()
        cursor.execute('select * from users')
        result = cursor.fetchall()
        conn.close()
        
    将mysql数据库的操作模块化:
    def on_open(cur=pymysql.cursors.DictCursor):
        conn = POOL.connection()
        cursor = conn.cursor(cursor=cur)
        return conn,cursor
    
    def on_close(conn,cursor):
        cursor.close()
        conn.close()
    
    def fetchone(sql,args,cur=pymysql.cursors.DictCursor):
        """
        获取单条数据
         获得的数据默认以字典的形式,如果要以元祖展示,传参时设置cur=None
        """
        conn,cursor = on_open(cur)
        cursor.execute(sql, args)
        result = cursor.fetchone()
        return result
    
    def fetchall(sql,args,cur=pymysql.cursors.DictCursor):
        """
        获取多条数据
        """
        conn, cursor = on_open(cur)
        cursor.execute(sql, args)
        result = cursor.fetchall()
        return result
    
    def exec_sql(sql,args,cur=pymysql.cursors.DictCursor):
        """
        添加/删除/修改
        :param sql: insert into table(%s,%s) values(....)
        """
        conn, cursor = on_open(cur)
        cursor.execute(sql, args)
        conn.commit()
    1.DBUtils的作用:
        创建数据库连接池
    2.简述数据库连接池的原理?
        1.启动时会在内存中维护一个连接池
        2.当请求需要连接数据库时则去连接池中获取一个连接,如果有空闲的连接就去获取
         没有则等待或报错
        3.使用完毕后,需要将连接归还到连接池中
    3.连接池在最开始启动时,最大连接数是100,是创建了100个链接吗?
    不是
    View Code

      wtforms:对用户请求数据做表单验证  

    pip3 install wtforms 
    https://www.cnblogs.com/wupeiqi/articles/8202357.html
    
    自定义校验类:
    from wtforms.fields import simple
    from wtforms.fields import html5
    from wtforms.fields import core
    from wtforms import widgets
    from wtforms import validators
    from wtforms import Form
    
    
    class TestForm(Form):
        name = simple.StringField(
            label='用户名',
            validators=[
                validators.DataRequired()
            ],  #错误信息
            widget=widgets.TextInput(),
            render_kw={'class': 'form-control'}  #错误信息样式
        )
        """
        pwd = simple.PasswordField(
            label='密码',
            validators=[
                validators.DataRequired(message='密码不能为空.')
            ],
            widget=widgets.PasswordInput(),
            render_kw={'class': 'form-control'}
        )
    
        pwd_confirm = simple.PasswordField(
            label='重复密码',
            validators=[
                validators.DataRequired(message='重复密码不能为空.'),
                validators.EqualTo('pwd', message="两次密码输入不一致")
            ], #自定义了比较
            widget=widgets.PasswordInput(),
            render_kw={'class': 'form-control'}
        )
    
        email = html5.EmailField(
            label='邮箱',
            validators=[
                validators.DataRequired(message='邮箱不能为空.'),
                validators.Email(message='邮箱格式错误')
            ],
            widget=widgets.TextInput(input_type='email'),
            render_kw={'class': 'form-control'}
        )
    
        gender = core.RadioField(
            label='性别',
            choices=(
                (1, ''),
                (2, ''),
            ),
            coerce=int
        )
        city = core.SelectField(
            label='城市',
            choices=(
                ('bj', '北京'),
                ('sh', '上海'),
            )
        )
    
        hobby = core.SelectMultipleField(
            label='爱好',
            choices=(
                (1, '篮球'),
                (2, '足球'),
            ),
            coerce=int
        )
    
        favor = core.SelectMultipleField(
            label='喜好',
            choices=(
                (1, '篮球'),
                (2, '足球'),
            ),
            widget=widgets.ListWidget(prefix_label=False),
            option_widget=widgets.CheckboxInput(),
            coerce=int
        )
        """
        cls_id = core.SelectField(
            label='请选择班级',
            choices=(
                ('bj', '北京'),
                ('sh', '上海'),
            )
            choices=[],
            coerce=int
        )
        #重写构造方法
        def __init__(self, *args, **kwargs):
            super(TestForm, self).__init__(*args, **kwargs)
    
            self.cls_id.choices = sqlhelper.fetchall(sql='select id,title from classes',args=[],cur=None)
        #钩子函数
        def validate_name(self,field):
            """
            对name进行验证时的钩子函数
            :param field:
            :return:
            """
            if field != 'root':
                raise validators.ValidationError("用户名必须是root")
    1.choices字段需要设置coerce=int 
    2.从数据库显示到页面上的数据要重写构造方法进行实时更新
    3.钩子函数:validate_字段名():pass 字段验证后执行定义的钩子函数
    服务端:
    1.get请求:
         if request.method == 'GET':
            form = TestForm()
            return render_template('add_cls.html',form=form)
    2.post请求。实例化时添加formdata再验证
    form = TestForm(formdata=request.form)
    if form.validate():
       数据都在form.data
       
    页面:
        <form method="post" novalidate>
            <p>
                {{form.title.label}} : {{form.title}} <span style="color: red">{{form.title.errors.0}}</span>
            </p>
            <input type="submit" value="添加">
        </form>
    View Code

       pipreqs:查看安装包依赖  

    pip3 install pipreqs 
    进入到项目里:pipreqs ./ --encoding=utf-8
    安装依赖
    install -r requirement.txt

    virtualenv 创建虚拟环境(纯净的环境)

    pip3 install virtualenv
    在公司中询问是否安装虚拟环境
        1.需要指定目录:virtualenv  文件名      virtualenv  venv
        2.激活 进入到新建项目中的scripts中执行activate  
        cd venvScripts  执行active
        命令提示符变了,有个(venv)前缀,表示当前环境是一个名为venv的Python环境。
        3.退出:deactivate
        此时就回到了正常的环境,现在pip或python均是在系统Python环境下执行。
    在linux中安装虚拟环境
    1. pip3 install virtualenv
    2.为virtualenv创建软连接
        ln -s /software/py3/bin/virtualenv /usr/bin/virtualenv1
        删除软连接:rm -rf /usr/bin/virtualenv1
    3.在当前目录下创建虚拟环境  virtualenv1  venv1
    4.激活 source venv1/bin/activate
    5.退出:deactivate

       偏函数  

    使用偏函数可以通过有效地“冻结”那些预先确定的参数,然后在运行时,当获得需要的剩余参数后,可以将他们解冻,传递到最终的参数中
    应用场景:调用sum函数时,我们已经知道了其中的一个参数,我们可以通过这个参数,重新绑定一个函数,然后加上其他参数去调用即可

    def add(a, b):
        return a + b;
    print(add(3, 5)) #8
    
    
    使用偏函数
    import functools
    def add(a,b):
        return a+b
    func1=functools.partial(add,3)
    print(func1)   #functools.partial(<function add at 0x0000028C9C2CFBF8>, 2)
    print(func1(5)) #8

       上下文管理   

    RequestContext:封装请求上下文的对象
    AppContext:封装应用上下文的对象
    LocalStack : 通过此类实例来操作Local对象,提供了pop、top、push方法
    Local:为每一个线程开辟一块独立的空间,用来存放数据
    LocalProxy:通过此类中的方法来取出request属性和session属性
    1.
    请求上下文:实例化一个RequestContext对象,并将请求的所有相关信息request和session封装到RequestContext对象中
    应用上下文:实例化一个AppContext对象,将current_app, g封装到AppContext中
    2.通过LocalStack类将RequestContext对象,AppContext对象添加到Local中(调用push方法)
    Local类为每一个线程开辟一块独立的空间,创建了一个字典来保存数据,这个字典的key是用线程的唯一标识存放自己的数据
    3.在视图函数中调用request时会执行LocalProxy中对应的魔法方法__getattr__或__setitem__
    又通过偏函数调用Localstark中top方法去Local获取到数据,取的是列表的最后一个值。
    4.请求终止时还是通过LocalStack的pop方法 将Local中将值在列表中pop掉

    内置的session流程:
    push中会调用session里面open_session方法,通过这个方法帮助我们获取用户原有的session信息,有就获取,没有就返回一个空的类似字典的数据结构,
    赋值给对象中的session,当使用的时候触发LocalProxy对像里对魔法方法通过偏函数用Localstark中方法去Local获取到数据
    使用完后,调用session对像的save_session方法,将数据加密写到用户的cookie中,这个操作在after_request之后
  • 相关阅读:
    互斥量
    读写锁
    死锁
    pthread
    线程
    守护进程
    信号捕捉
    信号集
    信号
    mmap
  • 原文地址:https://www.cnblogs.com/zgf-666/p/9528153.html
Copyright © 2020-2023  润新知