前言:

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

from werkzeug.wrappers import Request, Response #Flask的socket使用werkzeug实现,所以要导入 werkzeug

@Request.applicationdefhellow(request):return Response('Hello World')if __name__ == '__main__':from werkzeug.serving importrun_simple

run_simple('localhost',400,hellow)

Flask简单使用

from flask importFlask

app=Flask(__name__) #创建1个Flask实例

@app.route('/') #路由系统生成 视图对应url,1. decorator=app.route() 2. decorator(first_flask)

def first_flask(): #视图函数

return 'Hello World' #response

if __name__ == '__main__':

app.run()#启动socket

一、配置文件

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/')

from flask importFlask

app=Flask(__name__)

@app.route('/') #设置url传参数 http://127.0.0.1:5000/zhanggen

def first_flask(name): #视图必须有对应接收参数

print(name)return 'Hello World' #response

if __name__ == '__main__':

app.run()

接收字符串类型参数

@app.route('/post/')

#接收整型数字参数

app=Flask(__name__)

@app.route('//') #设置url传参数 http://127.0.0.1:5000/18/

def first_flask(age): #视图必须有对应接收参数

print(age)return 'Hello World' #response

if __name__ == '__main__':

app.run()

接收整型数字参数

@app.route('/post/')

#接收浮点型型数字参数

app=Flask(__name__)

@app.route('//') #设置url传参数http://127.0.0.1:5000/2345555.8889/

def first_flask(salary): #视图必须有对应接收参数

print(salary)return 'Hello World' #response

if __name__ == '__main__':

app.run()

接收浮点型数字参数

@app.route('/post/')

#接收URL链接类型参数

app=Flask(__name__)

@app.route('//') #设置url传参数:http://127.0.0.1:5000/http://www.baiu.com/

def first_flask(url): #视图必须有对应接收参数

print(url)return 'Hello World' #response

if __name__ == '__main__':

app.run()

接收URL链接类型参数

2、指定允许的请求方法

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

#指定允许的请求方法

app=Flask(__name__)

@app.route('//',methods=['get']) #只允许get请求

deffirst_flask(url):print(url)return 'Hello World' #response

if __name__ == '__main__':

app.run()

指定允许的请求方法

3、通过别名反向生成url

#反向生成url

from flask importFlask,url_for

app=Flask(__name__)

@app.route('/',endpoint='name1')deffirst_flask(url):print(url_for('name1',url=url)) #如果设置了url参数,url_for(别名,加参数)

return 'Hello World'

if __name__ == '__main__':

app.run()

url_for()反向生成url

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

#方式2通过app.add_url_rule()方法的方式调用路由

app=Flask(__name__)deffirst_flask():return 'Hello World'app.add_url_rule(rule='/index/',endpoint='name1',view_func=first_flask,methods=['GET'])#app.add_url_rule(rule=访问的url,endpoint=路由别名,view_func=视图名称,methods=[允许访问的方法])

if __name__ == '__main__':

app.run()

app.add_url_rule()方法调用路由

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

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

from flask importFlask, views, url_forfrom werkzeug.routing importBaseConverter

app= Flask(import_name=__name__)classRegexConverter(BaseConverter):"""自定义URL匹配正则表达式"""

def __init__(self, map, regex):

super(RegexConverter, self).__init__(map)

self.regex=regexdefto_python(self, value):"""路由匹配时,匹配成功后传递给视图函数中参数的值

:param value:

:return:"""

returnint(value)defto_url(self, value):"""使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数

:param value:

:return:"""val=super(RegexConverter, self).to_url(value)returnval#添加到flask中

app.url_map.converters['regex'] =RegexConverter

@app.route('/index/')defindex(nid):print(url_for('index', nid='888'))return 'Index'

if __name__ == '__main__':

app.run()

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

四、视图

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

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

#给Flask视图加装饰器#1、定义1个装饰器

defauth(func):print('我在上面')def inner(*args,**kwargs):return func(*args,**kwargs)returninner

app=Flask(__name__)

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

@auth#注意如果要给视图函数加装饰器,一点要加在路由装饰器下面,才会被路由装饰器装饰

deffirst_flask():print('ffff')return 'Hello World'

if __name__ == '__main__':

app.run()

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)

app.config['JSON_AS_ASCII']=False #指定json编码格式 如果为False 就不使用ascii编码,

app.config['JSONIFY_MIMETYPE'] ="application/json;charset=utf-8" #指定浏览器渲染的文件类型,和解码格式;

配置

方式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视图

from flask importFlask,url_for,views#-----------------------------------------------------

app=Flask(__name__) #装饰器

defauth(func):print('我在上面')def inner(*args,**kwargs):return func(*args,**kwargs)returninner#--------------------------------------------------------

class IndexView(views.MethodView): #CBV视图

methods=['GET'] #允许的http请求方法(改CBV只允许GET方法)

decorators = [auth,] #每次请求过来都加auth装饰器

defget(self):return 'Index.GET'

defpost(self):return 'Index.POST'app.add_url_rule('/index/',view_func=IndexView.as_view(name='name1')) #(name='name1'反向生成url别名

if __name__ == '__main__':

app.run()

CBV视图

五、模板语言

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

1.引用静态文件

方式1:别名引入

方式2:url_for()方法引入

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

变量

{{user_list}}

View Code

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

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

@app.template_global() #simple_tag

deffoo(arg):return ''

视图

{{foo(1)|safe}}

模板语言

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

@app.template_filter() #simple_fifter

deffoo1(arg1,arg2,arg3):return arg1+arg2+arg3

视图

{{ 'alex'|foo1('s','b',) }}

模板语言

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

3.0.简介

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

3.1. 安装

pip install wtforms #安装wtfroms插件

3.2.简单使用

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

用户登录页面验证

#!/usr/bin/env python#-*- coding:utf-8 -*-

from flask importFlask, render_template, request, redirectfrom wtforms importFormfrom wtforms.fields importcorefrom wtforms.fields importhtml5from wtforms.fields importsimplefrom wtforms importvalidatorsfrom wtforms importwidgets

app=Flask(__name__,template_folder='templates') #知道模板文件

app.debug=True#登录验证实例

classLoginForm(Form):#不同的字段 内部包含正则表达式 html5.EmailField | html5.DateTimeField...

name=simple.StringField(

label='用户名',

validators=[ #验证规则和错误提示信息

validators.DataRequired(message='用户名不能为空.'),

validators.Length(min=6, max=18, message='用户名长度必须大于%(min)d且小于%(max)d')

],

widget=widgets.TextInput(), #前端页面显示的插件.TextArea

render_kw={'class': 'form-control'} #设置form标签的class信息

)#不同的字段 内部包含正则表达式 html5.EmailField | html5.DateTimeField...

pwd =simple.PasswordField(

label='密码',

validators=[

validators.DataRequired(message='密码不能为空.'),

validators.Length(min=8, message='用户名长度必须大于%(min)d'),#自定义验证规则

validators.Regexp(regex="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{8,}",

message='密码至少8个字符,至少1个大写字母,1个小写字母,1个数字和1个特殊字符')

],

widget=widgets.PasswordInput(),

render_kw={'class': 'form-control'}

)

@app.route('/login/', methods=['GET', 'POST'])deflogin():if request.method == 'GET':

form= LoginForm() #实例化 form验证类

return render_template('login.html', form=form)else:

form= LoginForm(formdata=request.form)if form.validate(): #判断是否验证成功?

print('用户提交数据通过格式验证,提交的值为:', form.data)else:print(form.errors)return render_template('login.html', form=form)if __name__ == '__main__':

app.run()

app01.py

Title

登录

{{form.name.label}} {{form.name}} {{form.name.errors[0] }}

{{form.pwd.label}} {{form.pwd}} {{form.pwd.errors[0] }}

login.html

login.html

用户注册页面验证

#用户注册

from flask importFlask, render_template, request, redirectfrom wtforms importFormfrom wtforms.fields importcorefrom wtforms.fields importhtml5from wtforms.fields importsimplefrom wtforms importvalidatorsfrom wtforms importwidgets

app= Flask(__name__, template_folder='templates')

app.debug=TrueclassRegisterForm(Form):

name=simple.StringField(

label='用户名',

validators=[

validators.DataRequired()

],

widget=widgets.TextInput(),

render_kw={'class': 'form-control'},

default='张根' #设置input标签中默认值

)

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="两次密码输入不一致") #验证2次输入的密码是否一致?

],

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'), #生成email input标签

render_kw={'class': 'form-control'}

)

gender=core.RadioField(

label='性别',

choices=( #choice radio选项

(1, '男'),

(2, '女'),

),

coerce=int #讲用户提交过来的 '4' 强制转成 int 4

)

city=core.SelectField(

label='城市',

choices=(

('bj', '北京'),

('sh', '上海'),

)

)

hobby= core.SelectMultipleField( #select 下拉框多选框

label='爱好',

choices=(

(1, '篮球'),

(2, '足球'),

),

coerce=int

)

favor=core.SelectMultipleField(

label='喜好',

choices=(

(1, '篮球'),

(2, '足球'),

),

widget=widgets.ListWidget(prefix_label=False), #生成Checkbox 多选框

option_widget=widgets.CheckboxInput(),

coerce=int,

default=[1, 2]

)def __init__(self, *args, **kwargs): #重写form验证类的__init__方法可以实时同步数据中数据

super(RegisterForm, self).__init__(*args, **kwargs)

self.favor.choices= ((1, '篮球'), (2, '足球'), (3, '羽毛球'))def validate_pwd_confirm(self, field): #wtforms验证 钩子函数

"""自定义pwd_confirm字段规则,例:与pwd字段是否一致

:param field:

:return:"""

#最开始初始化时,self.data中已经有所有的值

if field.data != self.data['pwd']:#raise validators.ValidationError("密码不一致") # 继续后续验证

raise validators.StopValidation("密码不一致") #不再继续后续验证

@app.route('/register/', methods=['GET', 'POST'])defregister():if request.method == 'GET':

form= RegisterForm(data={'gender': 1}) #默认值

return render_template('register.html', form=form)else:

form= RegisterForm(formdata=request.form)ifform.validate():print('用户提交数据通过格式验证,提交的值为:', form.data)else:print(form.errors)return render_template('register.html', form=form)if __name__ == '__main__':

app.run()

app02.py

Title

用户注册

{% for item in form %}

{{item.label}}: {{item}} {{item.errors[0] }}

{% endfor %}

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__方法;

class InputText(object): #插件

def __str__(self):return ''

classInputPassword(object):def __str__(self):return ''

#-----------------------------------------------------------

class StringField(object): #字段

def __init__(self,wg):

self.widget=wgdef __str__(self): #调用插件的__str__

returnstr(self.widget)classDateField(object):def __init__(self, wg):

self.widget=wgdef __str__(self):returnstr(self.widget)#--------------------------------------------------------------

class LoginForm(object): #统一 灵活接口 (对象嵌套对象,多层封装)

name=StringField(wg=InputText()) #wg=InputText() 对象 StringField(wg=InputText())对象

pwd=DateField(wg=InputPassword())

l_obj=LoginForm()print(l_obj.name)print(l_obj.pwd)

调用关系

B.数据校验

后台定义好正则

用户发来数据

对数据进行校验

3.3.源码流程

生成HTML标签并显示

1.验证类(LogibForm)生成

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

'''class BaseForm():

pass

class NewBase(BaseForm,metaclass=FormMeta,):

pass

class Form(NewBase):

pass

class LoginForm(Form):

pass'''

class Form(with_metaclass(FormMeta,BaseForm)):

class BaseForm

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

classFormMeta(type):"""The metaclass for `Form` and any subclasses of `Form`.

`FormMeta`'s responsibility is to create the `_unbound_fields` list, which

is a list of `UnboundField` instances sorted by their order of

instantiation. The list is created at the first instantiation of the form.

If any fields are added/removed from the form, the list is cleared to be

re-generated on the next instantiation.

Any properties which begin with an underscore or are not `UnboundField`

instances are ignored by the metaclass."""

def __init__(cls, name, bases, attrs):

type.__init__(cls, name, bases, attrs) #继承type的功能

cls._unbound_fields = None #在LoginForm中添加1个静态字段

cls._wtforms_meta = None #在LoginForm中添加1个静态字段

class FormMeta

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

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

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

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

def __new__(cls, *args, **kwargs):#执行__new__方法

if '_form' in kwargs and '_name' inkwargs:return super(Field, cls).__new__(cls)else:#我x 没想到 ! __new__既然返回了1个 UnboundField()而不是StringField/PasswordField对象;狸猫换了太子 ?

return UnboundField(cls, *args, **kwargs)

Field.__new__()

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

classUnboundField(object):

_formfield=True

creation_counter= 0 #静态字段 设置计数器

def __init__(self, field_class, *args, **kwargs): #field_class=.StringField / PasswordField

#获取到field_class 的 参数封装到 UnboundField对象中,并且设置 排序 'creation_counter': 2

UnboundField.creation_counter+= 1 #每实例化1个 UnboundField对象 计数器+1

self.field_class =field_class

self.args=args

self.kwargs= kwargs #{'label': '用户名', 'validators': [, ], 'widget': , 'render_kw': {'class': 'form-control'}}

self.creation_counter = UnboundField.creation_counter#

'''print(self.__dict__)

{

'field_class': ,

'args': (),

'kwargs': {'label': '密码', 'validators': [, , ], 'widget': ,

'render_kw': {'class': 'form-control'}},

'creation_counter': 2

}'''

UnboundField.__init__

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

"""print(LoginForm.__dict__)

LoginForm ={

'__module__': '__main__',

'name': <1 UnboundField(StringField, (),{'creation_counter': 1, 'label': '用户名', 'validators': [, ], 'widget': , 'render_kw': {'class': 'form-control'} })>,

'pwd': <2 UnboundField(PasswordField, (),{'creation_counter': 2,'label': '密码', 'validators': [, , ], 'widget': , 'render_kw': {'class': 'form-control'}})>,

'__doc__': None,

'_unbound_fields': None,

'_wtforms_meta': None,

}"""

View Code

启发:

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

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

classQueen(object):def __new__(cls, *args, **kwargs): #类中__new__方法决定了类(),实例化出什么对象;

return Cat('狸猫','男','太子')def __init__(self,name): #由于__nwe__方法返回了其他对象,所以不会执行Queen的__init__方法

print('ok')

self.name=name

Prince=Queen('王子')print(Prince.name)print(Prince.gender)print(Prince.identity)

狸猫换太子

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属性

def __call__(cls, *args, **kwargs):if cls._unbound_fields is None: #在创建类时 已经设置LoginForm的_unbound_fields为空

fields =[]#获取LoginForm类中,中所有属性的key:[ '_get_translations', '_unbound_fields', '_wtforms_met,'name', 'populate_obj', 'process', 'pwd', 'validate'..... ]

for name indir(cls):if not name.startswith('_'): #排除__下划线的私有属性 name. pwd

unbound_field = getattr(cls, name) #cls =LoginForm类 #根据key 获取unbound_field 对象

if hasattr(unbound_field, '_formfield'): #检查unbound_field 对象是否包含_formfield = True

fields.append((name, unbound_field))#'''

#fields = [

#(name,name的unbound对象),

#(pwd,pwd的unbound对象),

#]

#'''

#对fields 按照定义顺序 进行排序

fields.sort(key=lambda x: (x[1].creation_counter, x[0])) #根据unbound对象的creation_counter进行字段排序

cls._unbound_fields =fieldsif cls._wtforms_meta isNone:

bases= [] #bases = [DefaultMeta],

#按照继承顺序:获取当前类(FormLogin)所有父类

for mro_class in cls.__mro__:if 'Meta' in mro_class.__dict__: #去每个父类(mro_class)获取 Meta = DefaultMeta

bases.append(mro_class.Meta)#bases = [DefaultMeta],

'''class Meta(DefaultMeta):

pass'''cls._wtforms_meta= type('Meta', tuple(bases), {}) #cls._wtforms_meta=Meta(DefaultMeta)类:

return type.__call__(cls, *args, **kwargs)

FormMeta.__call__

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

print(LoginForm.__dict__)

LoginForm={'__module__': '__main__','name': <1 UnboundField(StringField, (),{'creation_counter': 1, 'label': '用户名', 'validators': [, ], 'widget': , 'render_kw': {'class': 'form-control'} })>,'pwd': <2 UnboundField(PasswordField, (),{'creation_counter': 2,'label': '密码', 'validators': [, , ], 'widget': , 'render_kw': {'class': 'form-control'}})>,'__doc__': None,'_unbound_fields': [

(name, UnboundField对象(1,simple.StringField,参数)),

(pwd, UnboundField对象(2,simple.PasswordField,参数)),

],,'_wtforms_meta': Meta(DefaultMeta)类,

}"""

启发:

#sort排序

v1=[

(11,'Martin11',18),

(121,'Martin121',19),

(311,'Martin311',25),

(311, 'Martin311', 26) #按元素1排序,如果元素1相同按照 元素3排序

]

v1.sort(key=lambda x:(x[0],x[2])) #列表的sort方法,根据 列表中的元组元素 进行排序

print(v1)'''[(11, 'Martin11', 18), (121, 'Martin121', 19), (311, 'Martin311', 25), (311, 'Martin311', 26)]'''

列表的 sort()

classF1(object):pass

classF2(object):pass

classF3(F1):pass

classF4(F2,F3):pass

print(F4.__mro__) #打印F4 的继承关系

'''(

,

,

,

,

)'''

__mro__获取当前对象的继承顺序

2.2.执行LoginForm的__new__方法

没有__new__方法 pass

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

def __init__(self, formdata=None, obj=None, prefix='', data=None, meta=None, **kwargs):#实例化LoginForm中封装的 Meta类进行实例化,以后用于生成CSRF Tocken 标签

meta_obj =self._wtforms_meta()#meta是 form = LoginForm(meta={'csrf':'true'})传过来的参数,封装到meta_obj中

if meta is not None andisinstance(meta, dict):

meta_obj.update_values(meta)#执行父类的构造方法,参数

#self._unbound_fields

''''_unbound_fields'=[

(name, UnboundField对象(1,simple.StringField,参数)),

(pwd, UnboundField对象(2,simple.PasswordField,参数)),

],'''

#meta_ob=Meta(DefaultMeta)对象

super(Form, self).__init__(self._unbound_fields, meta=meta_obj, prefix=prefix)#给 form对象 中的_fields字段赋值如下;

'''_fields: {

name: StringField对象(),

pwd: PasswordField对象(),

}

name: StringField对象(widget=widgets.TextInput()),

pwd: PasswordField对象(widget=widgets.PasswordInput())'''

#循环form对象 中的_fields字段(字典),给form对象赋值 form.name/form.pwd

for name, field initeritems(self._fields):

setattr(self, name, field)'''_fields: {

name: StringField对象(),

pwd: PasswordField对象(),

}

name: StringField对象(widget=widgets.TextInput()),

pwd: PasswordField对象(widget=widgets.PasswordInput())'''self.process(formdata, obj, data=data, **kwargs)

Form.__init__

form ={

_fields: {

name: StringField对象(),

pwd: PasswordField对象(),

}

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

,无需form._fields['name'] / form._fields['name']

代码:

for name, field initeritems(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__()

def __str__(self):return self() #执行LoginForm的__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)做了什么?

defrender_field(self, field, render_kw):

other_kw= getattr(field, 'render_kw', None)if other_kw is notNone:

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 inkwargs:

kwargs['value'] =field._value()if 'required' not in kwargs and 'required' in getattr(field, 'flags', []):

kwargs['required'] =Truereturn HTMLString('' % 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."""

returniter(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功能

from flask importsessionimportjson

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

app.debug=True

app.secret_key='sjehfjeefrjewth43u' #设置session加密

app.config['JSON_AS_ASCII']=False #指定json编码格式 如果为False 就不使用ascii编码,

app.config['JSONIFY_MIMETYPE'] ="application/json;charset=utf-8" #指定浏览器渲染的文件类型,和解码格式;

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

msg= ''

if request.method=='POST':

name=request.values.get('user')

pwd=request.values.get('pwd')if name =='zhanggen' and pwd=='123.com':

session['user']=name #设置session的key value

return redirect('/index/')else:

msg='用户名或者密码错误'

return render_template('login.html',msg=msg)

@app.route('/index/',methods=['GET','POST'])defindex():

user_list= ['张根', 'egon', 'eric']

user=session.get('user') #获取session

ifuser:

user=['alex','egon','eric']returnjsonify(user_list)else:return redirect('/login/')if __name__ == '__main__':

app.run()

Flask自带session功能

2.第三方session组件(Session)

安装 pip install flask-session

from flask importsession, Flask,request,make_response,render_template,redirect,jsonify,Responsefrom flask.ext.session import Session #引入第三方session

importjson

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

app.debug=True

app.secret_key='sjehfjeefrjewth43u' #设置session加密

app.config['JSON_AS_ASCII']=False #指定json编码格式 如果为False 就不使用ascii编码,

app.config['JSONIFY_MIMETYPE'] ="application/json;charset=utf-8" #指定浏览器渲染的文件类型,和解码格式;

app.config['SESSION_TYPE']='redis'

from redis import Redis #引入连接 redis模块

app.config['SESSION_REDIS']=Redis(host='192.168.0.94',port=6379) #连接redis

Session(app)

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

msg= ''

if request.method=='POST':

name=request.values.get('user')

pwd=request.values.get('pwd')if name =='zhanggen' and pwd=='123.com':

session['user']=name #设置session的key value

return redirect('/index/')else:

msg='用户名或者密码错误'

return render_template('login.html',msg=msg)

@app.route('/index/',methods=['GET','POST'])defindex():

user_list= ['张根', 'egon', 'eric']

user=session.get('user') #获取session

ifuser:

user=['alex','egon','eric']returnjsonify(user_list)else:return redirect('/login/')if __name__ == '__main__':

app.run()

把session存到redis

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

def_get_interface(self, app):

config=app.config.copy()

config.setdefault('SESSION_TYPE', 'null')

config.setdefault('SESSION_PERMANENT', True)

config.setdefault('SESSION_USE_SIGNER', False)

config.setdefault('SESSION_KEY_PREFIX', 'session:')

config.setdefault('SESSION_REDIS', None)

config.setdefault('SESSION_MEMCACHED', None)

config.setdefault('SESSION_FILE_DIR',

os.path.join(os.getcwd(),'flask_session'))

config.setdefault('SESSION_FILE_THRESHOLD', 500)

config.setdefault('SESSION_FILE_MODE', 384)

config.setdefault('SESSION_MONGODB', None)

config.setdefault('SESSION_MONGODB_DB', 'flask_session')

config.setdefault('SESSION_MONGODB_COLLECT', 'sessions')

config.setdefault('SESSION_SQLALCHEMY', None)

config.setdefault('SESSION_SQLALCHEMY_TABLE', 'sessions')if config['SESSION_TYPE'] == 'redis':

session_interface=RedisSessionInterface(

config['SESSION_REDIS'], config['SESSION_KEY_PREFIX'],

config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])elif config['SESSION_TYPE'] == 'memcached':

session_interface=MemcachedSessionInterface(

config['SESSION_MEMCACHED'], config['SESSION_KEY_PREFIX'],

config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])elif config['SESSION_TYPE'] == 'filesystem':

session_interface=FileSystemSessionInterface(

config['SESSION_FILE_DIR'], config['SESSION_FILE_THRESHOLD'],

config['SESSION_FILE_MODE'], config['SESSION_KEY_PREFIX'],

config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])elif config['SESSION_TYPE'] == 'mongodb':

session_interface=MongoDBSessionInterface(

config['SESSION_MONGODB'], config['SESSION_MONGODB_DB'],

config['SESSION_MONGODB_COLLECT'],

config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'],

config['SESSION_PERMANENT'])elif config['SESSION_TYPE'] == 'sqlalchemy':

session_interface=SqlAlchemySessionInterface(

app, config['SESSION_SQLALCHEMY'],

config['SESSION_SQLALCHEMY_TABLE'],

config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'],

config['SESSION_PERMANENT'])else:

session_interface=NullSessionInterface()return session_interface

View Code

3.自定义session组件

#!/usr/bin/env python#-*- coding:utf-8 -*-

importuuidimportjsonfrom flask.sessions importSessionInterfacefrom flask.sessions importSessionMixinfrom itsdangerous importSigner, BadSignature, want_bytesclassMySession(dict, SessionMixin):def __init__(self, initial=None, sid=None):

self.sid=sid

self.initial=initial

super(MySession, self).__init__(initial or())def __setitem__(self, key, value):

super(MySession, self).__setitem__(key, value)def __getitem__(self, item):return super(MySession, self).__getitem__(item)def __delitem__(self, key):

super(MySession, self).__delitem__(key)classMySessionInterface(SessionInterface):

session_class=MySession

container={}def __init__(self):importredis

self.redis=redis.Redis()def_generate_sid(self):returnstr(uuid.uuid4())def_get_signer(self, app):if notapp.secret_key:returnNonereturn Signer(app.secret_key, salt='flask-session',

key_derivation='hmac')defopen_session(self, app, request):"""程序刚启动时执行,需要返回一个session对象"""sid=request.cookies.get(app.session_cookie_name)if notsid:

sid=self._generate_sid()return self.session_class(sid=sid)

signer=self._get_signer(app)try:

sid_as_bytes=signer.unsign(sid)

sid=sid_as_bytes.decode()exceptBadSignature:

sid=self._generate_sid()return self.session_class(sid=sid)#session保存在redis中

#val = self.redis.get(sid)

#session保存在内存中

val =self.container.get(sid)if val is notNone:try:

data=json.loads(val)return self.session_class(data, sid=sid)except:return self.session_class(sid=sid)return self.session_class(sid=sid)defsave_session(self, app, session, response):"""程序结束前执行,可以保存session中所有的值

如:

保存到resit

写入到用户cookie"""domain=self.get_cookie_domain(app)

path=self.get_cookie_path(app)

httponly=self.get_cookie_httponly(app)

secure=self.get_cookie_secure(app)

expires=self.get_expiration_time(app, session)

val=json.dumps(dict(session))#session保存在redis中

#self.redis.setex(name=session.sid, value=val, time=app.permanent_session_lifetime)

#session保存在内存中

self.container.setdefault(session.sid, val)

session_id=self._get_signer(app).sign(want_bytes(session.sid))

response.set_cookie(app.session_cookie_name, session_id,

expires=expires, httponly=httponly,

domain=domain, path=path, secure=secure)

组件

from flask importFlaskfrom flask importsessionfrom my_session importMySessionInterface

app= Flask(__name__)

app.secret_key= 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'app.session_interface=MySessionInterface()

@app.route('/login/', methods=['GET', "POST"])deflogin():print(session)

session['user1'] = 'alex'session['user2'] = 'alex'

del session['user2']return "内容"

if __name__ == '__main__':

app.run()

应用

七、蓝图

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

import luffy #导入luffy包就会执行luffy包中__init__.py文件

luffy.app.run()

app.py

from flask importFlask

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

app.debug=Truefrom .views importloginfrom .views importindex#把文件中蓝图对象注册到app里

app.register_blueprint(login.login,url_prefix='/login') #访问login蓝图必须以url_prefix开头

app.register_blueprint(index.index,url_prefix='/index')if __name__ == '__main__':

app.run()

luffy包的__init__.py

from flask import Blueprint #导入蓝图

login=Blueprint('login',__name__) #在本模块实例化1个蓝图

@login.route('/login/',methods=['GET','POST'])deflogin1():return '登录页面'

login

from flask importBlueprint

index=Blueprint('index',__name__)

@index.route('/index/',methods=['GET','POST'])defindex1():return '首页'

index

八、message (闪现)

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

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

from flask importFlask,request,flash,get_flashed_messages

app= Flask(__name__)

app.secret_key= 'some_secret'@app.route('/set/')defindex2():

flash('Disposable') #在message中设置1个个值

return 'ok'

#---------------------------------------------------------------------------------

@app.route('/')defindex1():

messages= get_flashed_messages() #获取message中设置的值,只能获取1次。(1次性)

print(messages)return "Index1"

if __name__ == "__main__":

app.run()

flask_message

九、中间件

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

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

2.@app.before_request:请求到达视图之前执行;(改函数不能有返回值,否则直接在当前返回)

3.@app.after_request:请求 经过视图之后执行;(最下面的先执行)

#!/usr/bin/env python#-*- coding:utf-8 -*-

from flask importFlask, Request, render_template

app= Flask(__name__, template_folder='templates')

app.debug=True

@app.before_first_request#第1个请求到来执行

defbefore_first_request1():print('before_first_request1')

@app.before_request#中间件2

defbefore_request1():

Request.nnn= 123

print('before_request1') #不能有返回值,一旦有返回值在当前返回

@app.before_requestdefbefore_request2():print('before_request2')

@app.errorhandler(404)defpage_not_found(error):return 'This page does not exist', 404@app.route('/')defhello_world():return "Hello World"@app.after_request#中间件 执行视图之后

defafter_request1(response):print('after_request1', response)returnresponse

@app.after_request#中间件 执行视图之后 先执行 after_request2

defafter_request2(response):print('after_request2', response)returnresponseif __name__ == '__main__':

app.run()

中间件

十、Flask相关组件

2、flask-script组件

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

pip install flask-script #安装

#!/usr/bin/env python#-*- coding:utf-8 -*-

from sansa importcreate_appfrom flask_script import Manager #导入

app =create_app()

manager=Manager(app) #实例化Manager对象

if __name__ == '__main__':

manager.run()

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 。

#!/usr/bin/env python#-*- coding:utf-8 -*-

from sansa importcreate_app,dbfrom flask_script import Manager #导入

from flask_migrate importMigrate,MigrateCommand

app=create_app()

manager=Manager(app) #实例化Manager对象

migrate=Migrate(app,db)

manager.add_command('db',MigrateCommand) #注册命令

if __name__ == '__main__':

manager.run()

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>

python flask web框架_Python_WEB框架之Flask相关推荐

  1. YOLOv5/v7 Flask Web 监测平台 | YOLOv5/v7 Flask Web 部署

    YOLOv7 Flask Web 监测平台图片效果展示 YOLOv7 Flask Web 监测平台视频效果展示 YOLOv7 Flask Web 检测平台 什么是Flask? 简介 Flask是一个轻 ...

  2. python原始web与django框架 mvc模式开发

    使用Python开发Web,最简单,原始和直接的办法是使用CGI标准. 首先做一个Python脚本 输出HTML代码 然后保存成.cgi扩展名的文件 通过浏览器访问此文件 如下: #!/usr/bin ...

  3. flask web如何加载html,Flask Web开发学习笔记(三)

    本文主要整理下几个Flask扩展: 0.Flask-Bootstrap:集成Twitter开发的一个开源框架Bootstrap. 1.Flask-Script:为Flask程序添加一个命令行解析器 2 ...

  4. 【Web开发】Python实现Web服务器(Ubuntu下Flask使用MySQL数据库)

  5. python的web框架哪个最好学_2018年要学习的10大Python Web框架

    通过为开发人员提供应用程序开发结构,框架使开发人员的生活更轻松.他们自动执行通用解决方案,缩短开发时间,并允许开发人员更多地关注应用程序逻辑而不是常规元素. 在本文中,我们分享了我们自己的前十大Pyt ...

  6. 【哈士奇赠书活动 - 18期】-〖Flask Web全栈开发实战〗

    文章目录 ⭐️ 赠书活动 - <Flask Web全栈开发实战> ⭐️ 编辑推荐 ⭐️ 内容提要 ⭐️ 赠书活动 → 获奖名单 ⭐️ 赠书活动 - <Flask Web全栈开发实战& ...

  7. python微型web框架flask介绍

    Flask是一个基于python的,微型web框架.之所以被称为微型是因为其核心非常简单,同时具有很强的扩展能力.它几乎不给使用者做任何技术决定. 安装flask时应该注意其必须的几个支持包比如Jin ...

  8. [Link]选择一个 Python Web 框架:Django vs Flask vs Pyramid

    选择一个 Python Web 框架:Django vs Flask vs Pyramid

  9. 比我的脸还干的gan货——Python Flask Web 框架入门

    Flask是一个轻量级的基于Python的web框架. 本文适合有一定HTML.Python.网络基础的同学阅读. 1. 简介 这份文档中的代码使用 Python 3 运行. 是的,所以读者需要自己在 ...

  10. API接口开发其实特简单,Python Flask Web 框架教程来了

    大家好,日常工作中,无论你是数据工程师.数据挖掘工程师,甚至数据分析人员,都不可避免的与他人进行数据交互,API接口提供数据是最常见的形式. 今天我给大家分享 Python Flask Web 框架教 ...

最新文章

  1. 基因组组装(Genome Assembly)
  2. mysql逻辑删除的问题_数据库表涉及-逻辑删除
  3. Python Django 查询单个对象API
  4. Java——去除字符串中的中文
  5. SpringSecurity分布式整合之认证模块搭建
  6. Android Notification总结
  7. 无限轮播图片的实现原理
  8. 【每日SQL打卡】​​​​​​​​​​​​​​​DAY 16丨报告的记录 II【难度中等】
  9. VBScript自动打卡
  10. 需求评审流程该如何高效的走下去
  11. 4-4Pytorch解决数字识别MNIST
  12. Facebook开源了两个无监督翻译模型,只用单语就能训练双语
  13. 【Mac OS】Anaconda+PyCharm搭建PyTorch环境
  14. Axure RP从入门到精通(二十)鼠标指针函数
  15. kali linux权限维持,msfconsole权限维持
  16. Elasticsearch系列:番外篇-Fielddata
  17. Qt程序的国际化支持【收藏】
  18. 教你把多个视频合并成一个视频的方法
  19. 【机器学习算法实践】AdaBoost是典型的Boosting算法,加法模型多个弱分类器流水线式的提升精度,更关注那些难处理的数据
  20. dhl软件_DHL的完整形式是什么?

热门文章

  1. matlab中点画线法代码,点划线规范画法,求中点画线算法的c++代码...
  2. Facebook账号注册需要注意什么?Facebook养号技巧?
  3. vue中使用router打开一个新的窗口
  4. R语言学习笔记5_参数的假设检验
  5. 一个女留学生在美国的七年
  6. win10计算机错误代码,win10电脑更新失败提示错误代码0x80070424修复方法
  7. 【网站点击流数据分析】01-项目业务背景
  8. 数据库连接报错 Communications link failure 连接失败解决办法
  9. English--基础知识点--3--动名词做主语
  10. centos yum 第三方仓库 scl 应用