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__

原文地址:https://www.cnblogs.com/ManyQian/p/9526630.html