• python3-开发进阶Flask的基础(4)


    今日内容:

    1. 上下文管理:LocalProxy对象
    2. 上下文管理:  请求上下文: request/session   app上下文:app/g
    3. 第三方组件:wtforms       1、使用     2、原理   

    一、LocalProxy

    首先我们一看一段自己写的代码:

    #test.py
    DATA={
        'request':{
            'method':'GET',
            'form':{}
        },
        'session':{
            'user':'duoduo',
            'age':'20'
        }
    }
    
    class LocalProxy(object):   #
    
        def __init__(self,key):
            self.key=key
    
    
        def get_dict(self):
            return DATA[self.key]
    
    
        def __str__(self):
            return 'duoduo'
    
        def __getattr__(self, item):
            data_dict=self.get_dict()
            return data_dict[item]
    
        def __getitem__(self, item):
            data_dict = self.get_dict()
            return data_dict[item]
    
        def __add__(self, other):
            return other+1
    
    
    request=LocalProxy('request')
    session=LocalProxy('session')
    LocalProxy的类就相当于一个代理

    下面我们来看看源码:先来个最简单flask

    from flask import Flask
    duo=Flask(__name__)
    
    @duo.route('/index/') 
    def index():
        return "hello world"
    
    if __name__ == '__main__':
        duo.run()

    我们把flask分为二个阶段,

    第一阶段:

    请求到来  先执行app.__call__ --->wsgi_app--->实例化RequestContext对象赋值给ctx

    在ctx中 ctx.request=Request(environ),ctx.session=None

    (具体描述:将request和Session 相关数据封装到 ctx=RequestContext对象中)

    ctx.push()------->_request_ctx_stack.push(self)   这个self就是包含了request,session的ctx

    _request_ctx_stack=LocalStack()  单例,全局变量,只有一个

    (具体描述:再通过LocalStack将ctx添加到Local中)

    存储的格式: __storage__={唯一字段:{’stack:[ctx(request,session)]‘}}  

    第二阶段:视图函数中获取request或session

      方式一:直接找LocalStack获取

    from flask.globals import  _request_ctx_stack
    #先这句写在视图函数中
    print(_request_ctx_stack.top.request.method)

      方式二:通过代理LocalProxy获取

    from flask import Flask,request
    print(request.method)

    他是怎么实现的,导入的request是在哪放着

    我们点开导入的request,看看是什么:

    我们猜想request.method有值,那么LocalProxy有method 或者是__getattr__方法,点开源码看看里边到底有没有

    没有method,那么就是另一这种可能,__getattr__

     

     调用偏函数

     

    二、上下文管理:  请求上下文: request/session   app上下文:app/g

    1、程序启动:

      我们创建两个Local:还有两个LocalStack,

      _request_ctx_stack

      _app_ctx_stack

    2、请求到来

      对数据进行封装

      ctx=RequestContext(request,session)

      app_ctx=AppContext(app,g)

      保存数据

      将包含了(app,g)数据的app_ctx对象,利用_app_ctx_stack=LocalStack()将app_ctx添加到Local中

        storage={'唯一标识:{stack:[app_ctx(app,g),]}'}

      将包含了request,session数据的ctx对象,利用_request_ctx_stack=LocakStack(),将ctx添加到Local中

        storage={'唯一标识:{stack:[ctx(request,session),]}'}

    3、视图函数处理:

    直接导入,然后相应的获取值,一个是请求上下文取值,一个是app上下文取值

    4、结束

    _app_ctx_stack.pop

    _request_ctx_stack.pop

    这下值就去取走了

    我们来看源码:

    from flask import globals  #点globals

    他们都在最开始就创建了local对象

     

    请求进来,执行__call__---》wsgi_app--->生成ctx对象----》ctx.push,再看这里的源码

    再来看看  AppContext

    才会去看看 app_ctx.push()

    现在我们提出一个问题:

    1、flask中g的生命周期?

    在请求来的时候g就创建了,当请求走的时候g的生命周期一结束,

    2、g和session一样吗?

    和session 是不一样的,session的值还存在cookie中,还是可以取值

    3、g和全局变量一样?

    不一样,全局变量程序启动就一直在内存中

    4、上面的演示都是单线程的,多线程的时候会变吗?

    他们存值时,都是以线程的唯一标志存值,不影响的

    三、flask 第三方组件 wtforms

    作用:1、生成html标签  2、form表单验证

    安装:

    pip3 install wtforms

    实例:

    import pymysql
    from DBUtils.PooledDB import PooledDB, SharedDBConnection
    import pymysql
    
    POOL = PooledDB(
            creator=pymysql,  # 使用链接数据库的模块
            maxconnections=6,  # 连接池允许的最大连接数,0和None表示不限制连接数
            mincached=2,  # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
            maxcached=5,  # 链接池中最多闲置的链接,0和None不限制
            maxshared=3,
            # 链接池中最多共享的链接数量,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='库名',
            charset='utf8'
        )
    
    def connect(type):
        conn = POOL.connection()
        cursor = conn.cursor(cursor=type)
        return conn,cursor
    
    
    def connect_close(conn,cursor):
        cursor.close()
        conn.close()
    
    def fetch_all(sql,args,type=pymysql.cursors.DictCursor):
        conn,cursor = connect(type)
    
        cursor.execute(sql, args)
        record_list = cursor.fetchall()
        connect_close(conn,cursor)
    
        return record_list
    
    
    def fetch_one(sql, args):
        conn, cursor = connect()
        cursor.execute(sql, args)
        result = cursor.fetchone()
        connect_close(conn, cursor)
    
        return result
    
    
    def insert(sql, args):
        conn, cursor = connect()
        row = cursor.execute(sql, args)
        conn.commit()
        connect_close(conn, cursor)
        return row
    helper.py
    from flask import Flask,request,render_template,session,current_app,g,redirect
    from wtforms import Form
    from wtforms.fields import simple
    from wtforms.fields import html5
    from wtforms.fields import core
    
    from wtforms import widgets
    from wtforms import validators
    
    app = Flask(__name__)
    
    
    class LoginForm(Form):
        name = simple.StringField(
            validators=[
                validators.DataRequired(message='用户名不能为空.'),
                # validators.Length(min=6, max=18, message='用户名长度必须大于%(min)d且小于%(max)d')
            ],
            widget=widgets.TextInput(),
            render_kw={'placeholder':'请输入用户名'}
        )
        pwd = simple.PasswordField(
            validators=[
                validators.DataRequired(message='密码不能为空.'),
                # validators.Length(min=8, message='用户名长度必须大于%(min)d'),
                # validators.Regexp(regex="^(?=.*[a-z])(?=.*[A-Z])(?=.*d)(?=.*[$@$!%*?&])[A-Za-zd$@$!%*?&]{8,}",
                #                   message='密码至少8个字符,至少1个大写字母,1个小写字母,1个数字和1个特殊字符')
    
            ],
            render_kw={'placeholder':'请输入密码'}
        )
    
    
    @app.route('/login',methods=['GET','POST'])
    def login():
        if request.method == "GET":
            form = LoginForm()
            # print(form.name,type(form.name)) # form.name是StringField()对象, StringField().__str__
            # print(form.pwd,type(form.pwd))   # form.pwd是PasswordField()对象,PasswordField().__str__
            return render_template('login.html',form=form)
    
        form = LoginForm(formdata=request.form)
        if form.validate():
            print(form.data)
            return redirect('https://www.luffycity.com/home')
        else:
            # print(form.errors)
            return render_template('login.html', form=form)
    
    
    class RegisterForm(Form):
        name = simple.StringField(
            label='用户名',
            validators=[
                validators.DataRequired()
            ],
            widget=widgets.TextInput(),
            render_kw={'class': 'form-control'},
            default='alex'
        )
    
        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 # int("1")
        )
        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,
            default=[1, ]
        )
    
    
    
    @app.route('/register', methods=['GET', 'POST'])
    def register():
        if request.method == 'GET':
            form = RegisterForm()
            return render_template('register.html', form=form)
    
        form = RegisterForm(formdata=request.form)
        if form.validate():
            print(form.data)
            return redirect('https://www.baidu.com')
    
        return render_template('register.html', form=form)
    
    import helper
    class UserForm(Form):
        city = core.SelectField(
            label='城市',
            choices=(),
            coerce=int
        )
        name = simple.StringField(label='姓名')
    
        def __init__(self,*args,**kwargs):
            super(UserForm,self).__init__(*args,**kwargs)
    
            self.city.choices=helper.fetch_all('select id,name from tb1',[],type=None)
    
    
    @app.route('/user')
    def user():
        if request.method == "GET":
            #form = UserForm(data={'name':'duoduo','city':3})
            form = UserForm()
            return render_template('user.html',form=form)
    
    if __name__ == '__main__':
        app.run()
    test.py

    先思考几个问题:

    1、form 对象为什么可以for循环?

    变成一个可迭代对象,类中含有__iter__,并且返回一个迭代器

    class Foo(object):
        # def __iter__(self):
        #     return iter([11,22,33])   #值是11,22,33
    
        def __iter__(self):
            yield 1
            yield 2
            yield 3
    obj=Foo()
    
    for item in obj:
        print(item)

    2、__new__方法返回值决定对象到底是什么

    class Bar(object):
        def __init__(self,cls):
            self.cls=cls
    
    class Foo(object):
        def __new__(cls, *args, **kwargs):
            #return super(Foo,cls).__new__(cls,*args,**kwargs)  <__main__.Foo object at 0x0000023741EEAC88>
            return  Bar(cls)    #<__main__.Bar object at 0x0000013FC1ABA4E0>
    
    obj=Foo()
    print(obj)

    3、metaclass   

    1. 创建类时,先执行type的__init__方法,当一个类实例化时,
    2. 执行type的__call__方法, 先执行类的__new__,创建对象再执行类的__init__,初始化
    3. __call__方法的返回值就是实例化的对象
    #类创建的两种方式      类是type创建的
    
    #1、
    # class Foo(object):
    #     a1=123
    #     def func(self):
    #         return 666
    
    # Foo=type('Foo',(object,),{'a1':123,'func':lambda self:666})
    
    #2、自定义type
    #
    # class MyType(type):
    #     pass
    #
    # class Foo(object,metaclass=MyType):  #指定当前类由谁创建
    #     a1=123
    #     def func(self):
    #         return 666
    
    # Foo=MyType('Foo',(object,),{'a1':123,'func':lambda self:666})
    
    #3、metaclass 作用
    #指定当前类由谁来创建
    
    
    class MyType(type):
        def __init__(self,*args,**kwargs):
            super(MyType,self).__init__(*args,**kwargs)
    
        def __call__(cls, *args, **kwargs):
            obj=cls.__new__(cls)
            cls.__init__(obj,*args, **kwargs)
            return obj
    
    class Foo(object,metaclass=MyType):
        a1=123
    
        def __init__(self):
            pass
    
        def __new__(cls, *args, **kwargs):
            return object.__new__(cls)
    
        def func(self):
            return 666
    
    #Foo是类
    #Foo是MyType的一个对象
    
    obj=Foo()

    4、wtforms的实现

    from wtforms import Form  #先点开Form

     

     我们创建的类没有metaclass但他继承的父类,有指定metaclass,就按照父类的来创建类

    看看FormMeta 中的__init__方法做了什么事?

     实例化时先执行__new__

     

     UnboundField类是做什么的?

     

    做完自己的操作,再执行父类的__call__,然后type会调用自己__new__方法,__init__方法

     下面Form 的序列化

    源码:

    类的创建  type.__init__

    对象的创建  type.__call__   1、类.__new__  2、类.__init__

  • 相关阅读:
    HDU4003 Find Metal Mineral
    POJ1125 Stockbroker Grapevine
    HDU4028The time of a day
    弱校ACM奋斗史
    POJ1236 Network of Schools
    HDU4004 The Frog's Games
    HDU4001 To Miss Our Children Time
    POJ2186 Popular Cows
    POJ1094 Sorting It All Out
    hadoop2.7.1单机和伪集群的搭建0
  • 原文地址:https://www.cnblogs.com/ManyQian/p/9526630.html
Copyright © 2020-2023  润新知