flask PythonWEB框架之Flask

前言:

Django:1个重武器,包含了web开发中常用的功能、组件的框架;(ORM、Session、Form、Admin、分页、中间件、信号、缓存、ContenType....);

Tornado:2大特性就是异步非阻塞、原生支持WebSocket协议;

Flask:封装功能不及Django完善,性能不及Tornado,但是Flask的第三方开源组件比丰富;http://flask.pocoo.org/extensions/

Bottle:比较简单;

总结:

都不是我写的!!!不论优劣,不同的工具而已;

小型web应用设计的功能点不多使用Flask;

大型web应用设计的功能点比较多使用的组件也会比较多,使用Django(自带功能多不用去找插件);

如果追求性能可以考虑Tornado;

Flask的socket是基于Werkzeug 实现的,模板语言依赖jinja2模板,在使用Flask之前需要安装一下;

pip3 install flask           #安装flask
  werkzeug

Flask简单使用

  View Code

一、配置文件

app=Flask(__name__,template_folder='templates',static_url_path='/static/',static_path='/zhanggen')

模板路径: template_folder='templates'

静态文件路径:static_url_path='/static/'

静态文件引入别名:static_path='/zhanggen'

设置为调试环境:app.debug=True (代码修改自动更新)

设置json编码格式 如果为False 就不使用ascii编码:app.config['JSON_AS_ASCII']=False 

设置响应头信息Content-Type   app.config['JSONIFY_MIMETYPE'] ="application/json;charset=utf-8"  (注意 ;charset=utf-8)

二、路由系统

1.动态路由(url传参)

@app.route('/user/<name>')

  接收字符串类型参数

@app.route('/post/<int:age>')

  接收整型数字参数

@app.route('/post/<float:salary>')

  接收浮点型数字参数

@app.route('/post/<path:path>')

  接收URL链接类型参数

2、指定允许的请求方法

@app.route('/login', methods=['GET', 'POST']) 

  指定允许的请求方法

3、通过别名反向生成url

  url_for()反向生成url

4、通过app.add_url_rule()调用路由

  app.add_url_rule()方法调用路由

5、扩展路由功能:正则匹配url

如果需要一些复杂的匹配规则可以自定义正则匹配url

  自定义正则表达式匹配路由

四、视图

1、给Flask视图函数加装饰器

注意如果要给视图函数加装饰器增加新功能,一点要加在路由装饰器下面,才会被路由装饰器装饰,才能生生成url关系;

  View Code

2、request和response

 a.请求相关信息

request.method: 获取请求方法

request.json

request.json.get("json_key"):获取json数据 **较常用      

request.argsget('name') :获取get请求参数   

request.form.get('name') :获取POST请求参数

request.form.getlist('name_list'):获取POST请求参数列表(多个)

request.values.get('age') :获取GET和POST请求携带的所有参数(GET/POST通用)

request.cookies.get('name'):获取cookies信息

request.headers.get('Host'):获取请求头相关信息

request.path:获取用户访问的url地址,例如(/,/login/,/ index/);

request.full_path:获取用户访问的完整url地址+参数 例如(/login/?age=18)

request.script_root: 抱歉,暂未理解其含义;

request.url:获取访问url地址,例如http://127.0.0.1:5000/?age=18;

request.base_url:获取访问url地址,例如 http://127.0.0.1:5000/;

request.url_root

request.host_url

request.host:获取主机地址

request.files:获取用户上传的文件

obj = request.files['the_file_name']

obj.save('/var/www/uploads/' + secure_filename(f.filename))  直接保存

 

 b、响应相关信息

return "字符串" :响应字符串

return render_template('html模板路径',**{}):响应模板

return redirect('/index.html'):跳转页面

响应json数据

方式1: return jsonify(user_list) 

  配置

方式2:

return Response(data,mimetype="application/json;charset=utf-8",)

如果需要设置响应头就需要借助make_response()方法

from flask import Flask,request,make_response           

response = make_response(render_template('index.html'))

response是flask.wrappers.Response类型

response.delete_cookie('key')

response.set_cookie('key', 'value')

response.headers['X-Something'] = 'A value'

return respons

3 、Flask之CBV视图

  CBV视图

 

五、模板语言

Flask使用的是Jinja2模板,所以其语法和Django无差别(Django的模板语言参考Jinja2)

1.引用静态文件

方式1:别名引入

<link rel="stylesheet" href="/zhanggen/commons.css">

方式2:url_for()方法引入

<link rel="stylesheet" href="{{  url_for('static',filename='commons.css')  }}">

2.模板语言引用上下文对象

变量

  View Code

循环、索引取值

  View Code

Flask的Jinjia2可以通过Context 把视图中的函数传递把模板语言中执行,这就是Django中的simple_tag和simple_fifter;

simple_tag(只能传2个参数,支持for、if)

  视图
  模板语言

simple_fifter(对参数个数无限制,不支持for、if)

  视图
  模板语言

3.wtform(flask表单验证插件)

3.0.简介

 wtformWTForms是一个支持多个web框架的form组件,主要对用户请求数据 进行表单验证。

3.1. 安装

pip install wtforms  #安装wtfroms插件

3.2.简单使用

wtforms和Django自带的form验证插件功能相同,使用起来大同小异;

用户登录页面验证

  app01.py
  login.html

用户注册页面验证 

  app02.py
  register.html

3.2.wtforms源码 猜想....

A.自动生成html标签

先来分析一下form验证类的结构

LoginForm类中包含了2个字段: name 和 pwd,而name / pwd字段 = 对象,所以LoginForm 类包含了2个对象;

如果实例化了obj=LoginForm() 就等于 在 这1个对象中嵌套了 2个对象;

前端使用Form验证插件:

那如果在前端for循环LoginForm对象,就等于调用LoginForm对象的__iter__方法,把每个字段(对象)封装的数据 返回

如果前端{{ obj }}= 直接调用了字段对象的__str__方法;

  调用关系

B.数据校验

后台定义好正则

用户发来数据

对数据进行校验

3.3.源码流程

生成HTML标签并显示

1.验证类(LogibForm)生成

1.1.由于 metaclass=FormMeta,所以LoginForm是由FormMeta创建的

  View Code

1.2.执行FormMeta 的__init__方法,在LoginForm中添加2个静态字段

  View Code

1.3.开始解释LoginForm中的 实例化字段对象name=simple.StringField()simple.PasswordField()

StringField/PasswordField开始实例化(提到实例化就应该想到:指定元类的__call__、自己/父类的__new__、__init__):

StringField/PasswordField是默认元类,自己没有__new__和__init__方法;

但父类Field类中有__new__方法,所以执行父类的__new__(Field.__new__)返回UnboundField对象

  Field.__new__()

由于Field.__new__方法返回了 1个 UnboundField对象,来看 UnboundField的__init__方法

  UnboundField.__init__

UnboundField的__init__方法在 UnboundField对象中封装了Field类的参数和计数器,所以现在LoginForml类中封装数据如下

复制代码
"""
print(LoginForm.__dict__)
LoginForm ={
    '__module__': '__main__', 
    'name': <1 UnboundField(StringField, (),{'creation_counter': 1, 'label': '用户名', 'validators': [<wtforms.validators.DataRequired object at 0x00000000037DAEB8>, <wtforms.validators.Length object at 0x000000000382B048>], 'widget': <wtforms.widgets.core.TextInput object at 0x000000000382B080>, 'render_kw': {'class': 'form-control'}  })>, 
    'pwd': <2 UnboundField(PasswordField, (),{'creation_counter': 2,'label': '密码', 'validators': [<wtforms.validators.DataRequired object at 0x000000000382B0F0>, <wtforms.validators.Length object at 0x000000000382B128>, <wtforms.validators.Regexp object at 0x000000000382B160>], 'widget': <wtforms.widgets.core.PasswordInput object at 0x000000000382B208>, 'render_kw': {'class': 'form-control'}})>, 
    '__doc__': None, 
    '_unbound_fields': None, 
    '_wtforms_meta': None,
}
"""
复制代码

启发:

不一定要把代码都写在当前类中,如过多个类和类之间有同性方法、属性可以抽出来集中到父类之中;子类继承父类所以子类实例化对象之后,继承享有2者的属性和方法;所以看源码遇到继承一点要注意 观察父类;

每个对象实例化(在排除MetaClass的情况下)都会执行 父类的__new__方法,再去执行__init__方法;而__new__实质性决定了实例化出来的对象是神马?

  狸猫换太子

2.LoginForm实例化

谈到类实例化应该先检查该类是否指定了 Meta类,如果指定了Meta类, 就需要先执行 (指定元类的__call__、自己/父类的__new__、__init__)

21.执行FormMeta的__call__方法,赋值LoginForm的_unbound_fields 和 _wtforms_meta属性;

根据unbound对象的creation_counter属性对 LoginForm中的字段进行排序,并填充到 LoginForm的_unbound_fields属性中

根据 LoginForm的__mro__继承顺序:获取当前类(FormLogin)所有父类,并在每个父类中 提取Meta属性添加到列表,转成元组,最后创建Meta类让其继承,赋值LoginForm._wtforms_meta属性

  FormMeta.__call__

 执行完了指定元类 FormMeta.__call__()方法之后的LoginForm类中封装的数据

复制代码
print(LoginForm.__dict__)
LoginForm ={
    '__module__': '__main__', 
    'name': <1 UnboundField(StringField, (),{'creation_counter': 1, 'label': '用户名', 'validators': [<wtforms.validators.DataRequired object at 0x00000000037DAEB8>, <wtforms.validators.Length object at 0x000000000382B048>], 'widget': <wtforms.widgets.core.TextInput object at 0x000000000382B080>, 'render_kw': {'class': 'form-control'}  })>, 
    'pwd': <2 UnboundField(PasswordField, (),{'creation_counter': 2,'label': '密码', 'validators': [<wtforms.validators.DataRequired object at 0x000000000382B0F0>, <wtforms.validators.Length object at 0x000000000382B128>, <wtforms.validators.Regexp object at 0x000000000382B160>], 'widget': <wtforms.widgets.core.PasswordInput object at 0x000000000382B208>, 'render_kw': {'class': 'form-control'}})>, 
    '__doc__': None, 
    
    '_unbound_fields': [
                (name, UnboundField对象(1,simple.StringField,参数)),
                (pwd, UnboundField对象(2,simple.PasswordField,参数)),
            ],, 
    '_wtforms_meta': Meta(DefaultMeta)类,
}
"""
复制代码

启发:

  列表的 sort()
  __mro__获取当前对象的继承顺序

2.2.执行LoginForm的__new__方法

没有__new__方法 pass

2.3.执行LoginForm的__init__方法实例化form对象

  Form.__init__

执行Form父类BaseForm.__init__方法,把UnboundField对象转换成StringField对象,并赋值到form对象的_fields:{}字典中;

  BaseForm. __init__
 form = {
        _fields: {
                name: StringField对象(),
                pwd: PasswordField对象(),
            }


循环form对象 中的_fields字段(字典),分别赋值到form对象,这样就可以通过form.name/form.pwd直接获取到Field对象了
,无需form._fields['name'] / form._fields['name']

代码:
for name, field in iteritems(self._fields):
    setattr(self, name, field)

                        form对象封装数据就变成以下内容喽
复制代码
 

form = { _fields: { name: StringField对象(), pwd: PasswordField对象(), } name: StringField对象(widget=widgets.TextInput()), pwd: PasswordField对象(widget=widgets.PasswordInput()) }
复制代码

3. 当form对象生成之后 print(form.name) = 执行StringField对象的__str__方法;

StringField类中没有__str__方法,所以去执行基类Field的,Field.__str__方法返回了:  self()  =  StringFieldObj.__call__()

  Field.__str__方法

StringField没有__call__所以执行其基类Field.__call__方法,调用了self.meta.render_field(self, kwargs)

    def __call__(self, **kwargs):            # self=StringField对象
        return self.meta.render_field(self, kwargs) #把StringField对象传传入meta.render_field方法

下面来看self.meta.render_field(self, kwargs)做了什么?

复制代码
  def render_field(self, field, render_kw):

        other_kw = getattr(field, 'render_kw', None)
        if other_kw is not None:
            render_kw = dict(other_kw, **render_kw)
        # StringField对象.widget(field, **render_kw)
        #插件.__call__()
        '''
        #field =StringField对象
           StringField对象.widget对象()=调用widget对象的.__call__方法
        '''
        return field.widget(field, **render_kw)
复制代码

 来看widget对象=TextInput()的__call__方法,最终打印了obj.name的结果

复制代码
  def __call__(self, field, **kwargs):
        kwargs.setdefault('id', field.id)
        kwargs.setdefault('type', self.input_type)
        if 'value' not in kwargs:
            kwargs['value'] = field._value()
        if 'required' not in kwargs and 'required' in getattr(field, 'flags', []):
            kwargs['required'] = True
        return HTMLString('<input %s>' % self.html_params(name=field.name, **kwargs))
复制代码
复制代码
   """
        0. Form.__iter__: 返回所有字段对象
            1. StringField对象.__str__
            2. StringField对象.__call__
            3. meta.render_field(StringField对象,)
            4. StringField对象.widget(field, **render_kw)
            5. 插件.__call__()
        """
复制代码

4.执行for iteam in form对象的执行流程

执行form对象基类BaseForm的__inter__方法,变量self._fields字典中的内容

复制代码
def __iter__(self):
        """Iterate form fields in creation order."""
        return iter(itervalues(self._fields))
_fields: {
                name: StringField对象(),
                pwd: PasswordField对象(),
            }
复制代码

用户输入数据的校验验证流程form = LoginForm(formdata=request.form)

复制代码
        # 请求发过来的值
        form = LoginForm(formdata=request.form) # 值.getlist('name')

        # 实例:编辑
        # # 从数据库对象
        # form = LoginForm(obj='值') # 值.name/值.pwd
        #
        # # 字典 {}
        # form = LoginForm(data=request.form) # 值['name']

        # 1. 循环所有的字段
        # 2. 获取每个字段的钩子函数
        # 3. 为每个字段执行他的验证流程 字段.validate(钩子函数+内置验证规则)
复制代码

六、session功能

1. Flask自带的session功能

  Flask自带session功能

2.第三方session组件(Session)

安装 pip install flask-session

  把session存到redis

不仅可以把session存放到redis还可放到文件、内存、memcache...

  View Code

3.自定义session组件

  组件
  应用

 七、蓝图

使用Flask自带Blueprintmuk模块,帮助我们做代码目录结构的归类

  app.py
  luffy包的__init__.py
  login
  index

 

八、message (闪现)

message是一个基于Session实现的用于保存数据的集合,其特点是:一次性。

特点:和labada匿名函数一样不长期占用内存

  flask_message

九、中间件

flask也有中间件功能和Django类似,不同的是使用的是使用3个装饰器来实现的;

1.@app.before_first_request :请求第1次到来执行1次,之后都不执行;

[email protected]_request:请求到达视图之前执行;(改函数不能有返回值,否则直接在当前返回)

[email protected]_request:请求 经过视图之后执行;(最下面的先执行)

  View Code

 

十、Flask相关组件

1、flask-sqlchemy

2、flask-script组件

flask-script组件:用于通过脚本的形式,启动 flask;(实现类似Django的python manager.py runserver 0.0.0.0:8001)

pip install flask-script        #安装
  run.py
 
  

python run.py runserver -h 0.0.0.0 -p 8001

* Running on http://0.0.0.0:8001/ (Press CTRL+C to quit)

 

3.flask-migrate组件

在线修改、迁移数据库(Django的 migrate 。

  run.py
pip install flask-migrate       #安装

3.1.初始化数据库:python run.py db init

3.2.迁移数据:       python run.py db migrate

3.3.生成表:           python run.py db upgrade  

ps:修改表结构 first 直接注释静态字段代码,second 执行 python run.py db upgrade.

复制代码
D:\Flask练习\sansa>python run.py db init
Creating directory D:\Flask练习\sansa\migrations ... done
Creating directory D:\Flask练习\sansa\migrations\versions ... done
Generating D:\Flask练习\sansa\migrations\alembic.ini ... done
Generating D:\Flask练习\sansa\migrations\env.py ... done
Generating D:\Flask练习\sansa\migrations\README ... done
Generating D:\Flask练习\sansa\migrations\script.py.mako ... done
Please edit configuration/connection/logging settings in 'D:\\Flask练习\\sansa\\migrations\\alembic.ini' before proceeding.

D:\Flask练习\sansa>python run.py db migrate
INFO  [alembic.runtime.migration] Context impl MySQLImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.autogenerate.compare] Detected added table 'users666'
Generating D:\Flask练习\sansa\migrations\versions\a7f412a8146f_.py ... done

D:\Flask练习\sansa>python run.py db upgrade
INFO  [alembic.runtime.migration] Context impl MySQLImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.runtime.migration] Running upgrade  -> a7f412a8146f, empty message

D:\Flask练习\sansa>

前言:

Django:1个重武器,包含了web开发中常用的功能、组件的框架;(ORM、Session、Form、Admin、分页、中间件、信号、缓存、ContenType....);

Tornado:2大特性就是异步非阻塞、原生支持WebSocket协议;

Flask:封装功能不及Django完善,性能不及Tornado,但是Flask的第三方开源组件比丰富;http://flask.pocoo.org/extensions/

Bottle:比较简单;

总结:

都不是我写的!!!不论优劣,不同的工具而已;

小型web应用设计的功能点不多使用Flask;

大型web应用设计的功能点比较多使用的组件也会比较多,使用Django(自带功能多不用去找插件);

如果追求性能可以考虑Tornado;

Flask的socket是基于Werkzeug 实现的,模板语言依赖jinja2模板,在使用Flask之前需要安装一下;

pip3 install flask           #安装flask
  werkzeug

Flask简单使用

  View Code

一、配置文件

app=Flask(__name__,template_folder='templates',static_url_path='/static/',static_path='/zhanggen')

模板路径: template_folder='templates'

静态文件路径:static_url_path='/static/'

静态文件引入别名:static_path='/zhanggen'

设置为调试环境:app.debug=True (代码修改自动更新)

设置json编码格式 如果为False 就不使用ascii编码:app.config['JSON_AS_ASCII']=False 

设置响应头信息Content-Type   app.config['JSONIFY_MIMETYPE'] ="application/json;charset=utf-8"  (注意 ;charset=utf-8)

二、路由系统

1.动态路由(url传参)

@app.route('/user/<name>')

  接收字符串类型参数

@app.route('/post/<int:age>')

  接收整型数字参数

@app.route('/post/<float:salary>')

  接收浮点型数字参数

@app.route('/post/<path:path>')

  接收URL链接类型参数

2、指定允许的请求方法

@app.route('/login', methods=['GET', 'POST']) 

  指定允许的请求方法

3、通过别名反向生成url

  url_for()反向生成url

4、通过app.add_url_rule()调用路由

  app.add_url_rule()方法调用路由

5、扩展路由功能:正则匹配url

如果需要一些复杂的匹配规则可以自定义正则匹配url

  自定义正则表达式匹配路由

四、视图

1、给Flask视图函数加装饰器

注意如果要给视图函数加装饰器增加新功能,一点要加在路由装饰器下面,才会被路由装饰器装饰,才能生生成url关系;

  View Code

2、request和response

 a.请求相关信息

request.method: 获取请求方法

request.json

request.json.get("json_key"):获取json数据 **较常用      

request.argsget('name') :获取get请求参数   

request.form.get('name') :获取POST请求参数

request.form.getlist('name_list'):获取POST请求参数列表(多个)

request.values.get('age') :获取GET和POST请求携带的所有参数(GET/POST通用)

request.cookies.get('name'):获取cookies信息

request.headers.get('Host'):获取请求头相关信息

request.path:获取用户访问的url地址,例如(/,/login/,/ index/);

request.full_path:获取用户访问的完整url地址+参数 例如(/login/?age=18)

request.script_root: 抱歉,暂未理解其含义;

request.url:获取访问url地址,例如http://127.0.0.1:5000/?age=18;

request.base_url:获取访问url地址,例如 http://127.0.0.1:5000/;

request.url_root

request.host_url

request.host:获取主机地址

request.files:获取用户上传的文件

obj = request.files['the_file_name']

obj.save('/var/www/uploads/' + secure_filename(f.filename))  直接保存

 

 b、响应相关信息

return "字符串" :响应字符串

return render_template('html模板路径',**{}):响应模板

return redirect('/index.html'):跳转页面

响应json数据

方式1: return jsonify(user_list) 

  配置

方式2:

return Response(data,mimetype="application/json;charset=utf-8",)

如果需要设置响应头就需要借助make_response()方法

from flask import Flask,request,make_response           

response = make_response(render_template('index.html'))

response是flask.wrappers.Response类型

response.delete_cookie('key')

response.set_cookie('key', 'value')

response.headers['X-Something'] = 'A value'

return respons

3 、Flask之CBV视图

  CBV视图

 

五、模板语言

Flask使用的是Jinja2模板,所以其语法和Django无差别(Django的模板语言参考Jinja2)

1.引用静态文件

方式1:别名引入

<link rel="stylesheet" href="/zhanggen/commons.css">

方式2:url_for()方法引入

<link rel="stylesheet" href="{{  url_for('static',filename='commons.css')  }}">

2.模板语言引用上下文对象

变量

  View Code

循环、索引取值

  View Code

Flask的Jinjia2可以通过Context 把视图中的函数传递把模板语言中执行,这就是Django中的simple_tag和simple_fifter;

simple_tag(只能传2个参数,支持for、if)

  视图
  模板语言

simple_fifter(对参数个数无限制,不支持for、if)

  视图
  模板语言

3.wtform(flask表单验证插件)

3.0.简介

 wtformWTForms是一个支持多个web框架的form组件,主要对用户请求数据 进行表单验证。

3.1. 安装

pip install wtforms  #安装wtfroms插件

3.2.简单使用

wtforms和Django自带的form验证插件功能相同,使用起来大同小异;

用户登录页面验证

  app01.py
  login.html

用户注册页面验证 

  app02.py
  register.html

3.2.wtforms源码 猜想....

A.自动生成html标签

先来分析一下form验证类的结构

LoginForm类中包含了2个字段: name 和 pwd,而name / pwd字段 = 对象,所以LoginForm 类包含了2个对象;

如果实例化了obj=LoginForm() 就等于 在 这1个对象中嵌套了 2个对象;

前端使用Form验证插件:

那如果在前端for循环LoginForm对象,就等于调用LoginForm对象的__iter__方法,把每个字段(对象)封装的数据 返回

如果前端{{ obj }}= 直接调用了字段对象的__str__方法;

  调用关系

B.数据校验

后台定义好正则

用户发来数据

对数据进行校验

3.3.源码流程

生成HTML标签并显示

1.验证类(LogibForm)生成

1.1.由于 metaclass=FormMeta,所以LoginForm是由FormMeta创建的

  View Code

1.2.执行FormMeta 的__init__方法,在LoginForm中添加2个静态字段

  View Code

1.3.开始解释LoginForm中的 实例化字段对象name=simple.StringField()simple.PasswordField()

StringField/PasswordField开始实例化(提到实例化就应该想到:指定元类的__call__、自己/父类的__new__、__init__):

StringField/PasswordField是默认元类,自己没有__new__和__init__方法;

但父类Field类中有__new__方法,所以执行父类的__new__(Field.__new__)返回UnboundField对象

  Field.__new__()

由于Field.__new__方法返回了 1个 UnboundField对象,来看 UnboundField的__init__方法

  UnboundField.__init__

UnboundField的__init__方法在 UnboundField对象中封装了Field类的参数和计数器,所以现在LoginForml类中封装数据如下

复制代码
"""
print(LoginForm.__dict__)
LoginForm ={
    '__module__': '__main__', 
    'name': <1 UnboundField(StringField, (),{'creation_counter': 1, 'label': '用户名', 'validators': [<wtforms.validators.DataRequired object at 0x00000000037DAEB8>, <wtforms.validators.Length object at 0x000000000382B048>], 'widget': <wtforms.widgets.core.TextInput object at 0x000000000382B080>, 'render_kw': {'class': 'form-control'}  })>, 
    'pwd': <2 UnboundField(PasswordField, (),{'creation_counter': 2,'label': '密码', 'validators': [<wtforms.validators.DataRequired object at 0x000000000382B0F0>, <wtforms.validators.Length object at 0x000000000382B128>, <wtforms.validators.Regexp object at 0x000000000382B160>], 'widget': <wtforms.widgets.core.PasswordInput object at 0x000000000382B208>, 'render_kw': {'class': 'form-control'}})>, 
    '__doc__': None, 
    '_unbound_fields': None, 
    '_wtforms_meta': None,
}
"""
复制代码

启发:

不一定要把代码都写在当前类中,如过多个类和类之间有同性方法、属性可以抽出来集中到父类之中;子类继承父类所以子类实例化对象之后,继承享有2者的属性和方法;所以看源码遇到继承一点要注意 观察父类;

每个对象实例化(在排除MetaClass的情况下)都会执行 父类的__new__方法,再去执行__init__方法;而__new__实质性决定了实例化出来的对象是神马?

  狸猫换太子

2.LoginForm实例化

谈到类实例化应该先检查该类是否指定了 Meta类,如果指定了Meta类, 就需要先执行 (指定元类的__call__、自己/父类的__new__、__init__)

21.执行FormMeta的__call__方法,赋值LoginForm的_unbound_fields 和 _wtforms_meta属性;

根据unbound对象的creation_counter属性对 LoginForm中的字段进行排序,并填充到 LoginForm的_unbound_fields属性中

根据 LoginForm的__mro__继承顺序:获取当前类(FormLogin)所有父类,并在每个父类中 提取Meta属性添加到列表,转成元组,最后创建Meta类让其继承,赋值LoginForm._wtforms_meta属性

  FormMeta.__call__

 执行完了指定元类 FormMeta.__call__()方法之后的LoginForm类中封装的数据

复制代码
print(LoginForm.__dict__)
LoginForm ={
    '__module__': '__main__', 
    'name': <1 UnboundField(StringField, (),{'creation_counter': 1, 'label': '用户名', 'validators': [<wtforms.validators.DataRequired object at 0x00000000037DAEB8>, <wtforms.validators.Length object at 0x000000000382B048>], 'widget': <wtforms.widgets.core.TextInput object at 0x000000000382B080>, 'render_kw': {'class': 'form-control'}  })>, 
    'pwd': <2 UnboundField(PasswordField, (),{'creation_counter': 2,'label': '密码', 'validators': [<wtforms.validators.DataRequired object at 0x000000000382B0F0>, <wtforms.validators.Length object at 0x000000000382B128>, <wtforms.validators.Regexp object at 0x000000000382B160>], 'widget': <wtforms.widgets.core.PasswordInput object at 0x000000000382B208>, 'render_kw': {'class': 'form-control'}})>, 
    '__doc__': None, 
    
    '_unbound_fields': [
                (name, UnboundField对象(1,simple.StringField,参数)),
                (pwd, UnboundField对象(2,simple.PasswordField,参数)),
            ],, 
    '_wtforms_meta': Meta(DefaultMeta)类,
}
"""
复制代码

启发:

  列表的 sort()
  __mro__获取当前对象的继承顺序

2.2.执行LoginForm的__new__方法

没有__new__方法 pass

2.3.执行LoginForm的__init__方法实例化form对象

  Form.__init__

执行Form父类BaseForm.__init__方法,把UnboundField对象转换成StringField对象,并赋值到form对象的_fields:{}字典中;

  BaseForm. __init__
 form = {
        _fields: {
                name: StringField对象(),
                pwd: PasswordField对象(),
            }


循环form对象 中的_fields字段(字典),分别赋值到form对象,这样就可以通过form.name/form.pwd直接获取到Field对象了
,无需form._fields['name'] / form._fields['name']

代码:
for name, field in iteritems(self._fields):
    setattr(self, name, field)

                        form对象封装数据就变成以下内容喽
复制代码
 

form = { _fields: { name: StringField对象(), pwd: PasswordField对象(), } name: StringField对象(widget=widgets.TextInput()), pwd: PasswordField对象(widget=widgets.PasswordInput()) }
复制代码

3. 当form对象生成之后 print(form.name) = 执行StringField对象的__str__方法;

StringField类中没有__str__方法,所以去执行基类Field的,Field.__str__方法返回了:  self()  =  StringFieldObj.__call__()

  Field.__str__方法

StringField没有__call__所以执行其基类Field.__call__方法,调用了self.meta.render_field(self, kwargs)

    def __call__(self, **kwargs):            # self=StringField对象
        return self.meta.render_field(self, kwargs) #把StringField对象传传入meta.render_field方法

下面来看self.meta.render_field(self, kwargs)做了什么?

复制代码
  def render_field(self, field, render_kw):

        other_kw = getattr(field, 'render_kw', None)
        if other_kw is not None:
            render_kw = dict(other_kw, **render_kw)
        # StringField对象.widget(field, **render_kw)
        #插件.__call__()
        '''
        #field =StringField对象
           StringField对象.widget对象()=调用widget对象的.__call__方法
        '''
        return field.widget(field, **render_kw)
复制代码

 来看widget对象=TextInput()的__call__方法,最终打印了obj.name的结果

复制代码
  def __call__(self, field, **kwargs):
        kwargs.setdefault('id', field.id)
        kwargs.setdefault('type', self.input_type)
        if 'value' not in kwargs:
            kwargs['value'] = field._value()
        if 'required' not in kwargs and 'required' in getattr(field, 'flags', []):
            kwargs['required'] = True
        return HTMLString('<input %s>' % self.html_params(name=field.name, **kwargs))
复制代码
复制代码
   """
        0. Form.__iter__: 返回所有字段对象
            1. StringField对象.__str__
            2. StringField对象.__call__
            3. meta.render_field(StringField对象,)
            4. StringField对象.widget(field, **render_kw)
            5. 插件.__call__()
        """
复制代码

4.执行for iteam in form对象的执行流程

执行form对象基类BaseForm的__inter__方法,变量self._fields字典中的内容

复制代码
def __iter__(self):
        """Iterate form fields in creation order."""
        return iter(itervalues(self._fields))
_fields: {
                name: StringField对象(),
                pwd: PasswordField对象(),
            }
复制代码

用户输入数据的校验验证流程form = LoginForm(formdata=request.form)

复制代码
        # 请求发过来的值
        form = LoginForm(formdata=request.form) # 值.getlist('name')

        # 实例:编辑
        # # 从数据库对象
        # form = LoginForm(obj='值') # 值.name/值.pwd
        #
        # # 字典 {}
        # form = LoginForm(data=request.form) # 值['name']

        # 1. 循环所有的字段
        # 2. 获取每个字段的钩子函数
        # 3. 为每个字段执行他的验证流程 字段.validate(钩子函数+内置验证规则)
复制代码

六、session功能

1. Flask自带的session功能

  Flask自带session功能

2.第三方session组件(Session)

安装 pip install flask-session

  把session存到redis

不仅可以把session存放到redis还可放到文件、内存、memcache...

  View Code

3.自定义session组件

  组件
  应用

 七、蓝图

使用Flask自带Blueprintmuk模块,帮助我们做代码目录结构的归类

  app.py
  luffy包的__init__.py
  login
  index

 

八、message (闪现)

message是一个基于Session实现的用于保存数据的集合,其特点是:一次性。

特点:和labada匿名函数一样不长期占用内存

  flask_message

九、中间件

flask也有中间件功能和Django类似,不同的是使用的是使用3个装饰器来实现的;

1.@app.before_first_request :请求第1次到来执行1次,之后都不执行;

[email protected]_request:请求到达视图之前执行;(改函数不能有返回值,否则直接在当前返回)

[email protected]_request:请求 经过视图之后执行;(最下面的先执行)

  View Code

 

十、Flask相关组件

1、flask-sqlchemy

2、flask-script组件

flask-script组件:用于通过脚本的形式,启动 flask;(实现类似Django的python manager.py runserver 0.0.0.0:8001)

pip install flask-script        #安装
  run.py
 
 

python run.py runserver -h 0.0.0.0 -p 8001

* Running on http://0.0.0.0:8001/ (Press CTRL+C to quit)

 

3.flask-migrate组件

在线修改、迁移数据库(Django的 migrate 。

  run.py
pip install flask-migrate       #安装

3.1.初始化数据库:python run.py db init

3.2.迁移数据:       python run.py db migrate

3.3.生成表:           python run.py db upgrade  

ps:修改表结构 first 直接注释静态字段代码,second 执行 python run.py db upgrade.

复制代码
D:\Flask练习\sansa>python run.py db init
Creating directory D:\Flask练习\sansa\migrations ... done
Creating directory D:\Flask练习\sansa\migrations\versions ... done
Generating D:\Flask练习\sansa\migrations\alembic.ini ... done
Generating D:\Flask练习\sansa\migrations\env.py ... done
Generating D:\Flask练习\sansa\migrations\README ... done
Generating D:\Flask练习\sansa\migrations\script.py.mako ... done
Please edit configuration/connection/logging settings in 'D:\\Flask练习\\sansa\\migrations\\alembic.ini' before proceeding.

D:\Flask练习\sansa>python run.py db migrate
INFO  [alembic.runtime.migration] Context impl MySQLImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.autogenerate.compare] Detected added table 'users666'
Generating D:\Flask练习\sansa\migrations\versions\a7f412a8146f_.py ... done

D:\Flask练习\sansa>python run.py db upgrade
INFO  [alembic.runtime.migration] Context impl MySQLImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.runtime.migration] Running upgrade  -> a7f412a8146f, empty message

D:\Flask练习\sansa>

猜你喜欢

转载自www.cnblogs.com/lzqrkn/p/10274303.html