前言:

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之前需要安装一下;

  1. pip3 install flask #安装flask
  1. from werkzeug.wrappers import Request, Response # Flask的socket使用werkzeug实现,所以要导入 werkzeug
  2.  
  3. @Request.application
  4.  
  5. def hellow(request):
  6. return Response('Hello World')
  7.  
  8. if __name__ == '__main__':
  9. from werkzeug.serving import run_simple
  10. run_simple('localhost',400,hellow)

werkzeug

Flask简单使用

  1. from flask import Flask
  2.  
  3. app=Flask(__name__) #创建1个Flask实例
  4.  
  5. @app.route('/') #路由系统生成 视图对应url,1. decorator=app.route() 2. decorator(first_flask)
  6. def first_flask(): #视图函数
  7. return 'Hello World' #response
  8.  
  9. if __name__ == '__main__':
  10. app.run() #启动socket

一、配置文件

  1. 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>')

  1. from flask import Flask
  2.  
  3. app=Flask(__name__)
  4.  
  5. @app.route('/<name>') #设置url传参数 http://127.0.0.1:5000/zhanggen
  6. def first_flask(name): #视图必须有对应接收参数
  7. print(name)
  8. return 'Hello World' #response
  9.  
  10. if __name__ == '__main__':
  11. app.run()

接收字符串类型参数

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

  1. #接收整型数字参数
  2. app=Flask(__name__)
  3. @app.route('/<int:age>/') #设置url传参数 http://127.0.0.1:5000/18/
  4. def first_flask(age): #视图必须有对应接收参数
  5. print(age)
  6. return 'Hello World' #response
  7.  
  8. if __name__ == '__main__':
  9. app.run()

接收整型数字参数

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

  1. #接收浮点型型数字参数
  2. app=Flask(__name__)
  3. @app.route('/<float:salary>/') #设置url传参数http://127.0.0.1:5000/2345555.8889/
  4. def first_flask(salary): #视图必须有对应接收参数
  5. print(salary)
  6. return 'Hello World' #response
  7.  
  8. if __name__ == '__main__':
  9. app.run()

接收浮点型数字参数

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

  1. # 接收URL链接类型参数
  2. app=Flask(__name__)
  3. @app.route('/<path:url>/') #设置url传参数:http://127.0.0.1:5000/http://www.baiu.com/
  4. def first_flask(url): #视图必须有对应接收参数
  5. print(url)
  6. return 'Hello World' #response
  7.  
  8. if __name__ == '__main__':
  9. app.run()

接收URL链接类型参数

2、指定允许的请求方法

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

  1. # 指定允许的请求方法
  2. app=Flask(__name__)
  3. @app.route('/<path:url>/',methods=['get']) #只允许get请求
  4. def first_flask(url):
  5. print(url)
  6. return 'Hello World' #response
  7.  
  8. if __name__ == '__main__':
  9. app.run()

指定允许的请求方法

3、通过别名反向生成url

  1. #反向生成url
  2. from flask import Flask,url_for
  3. app=Flask(__name__)
  4. @app.route('/<path:url>',endpoint='name1')
  5. def first_flask(url):
  6. print(url_for('name1',url=url)) #如果设置了url参数,url_for(别名,加参数)
  7. return 'Hello World'
  8.  
  9. if __name__ == '__main__':
  10. app.run()

url_for()反向生成url

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

  1. #方式2通过app.add_url_rule()方法的方式调用路由
  2. app=Flask(__name__)
  3.  
  4. def first_flask():
  5. return 'Hello World'
  6.  
  7. app.add_url_rule(rule='/index/',endpoint='name1',view_func=first_flask,methods=['GET'])
  8. #app.add_url_rule(rule=访问的url,endpoint=路由别名,view_func=视图名称,methods=[允许访问的方法])
  9. if __name__ == '__main__':
  10. app.run()

app.add_url_rule()方法调用路由

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

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

  1. from flask import Flask, views, url_for
  2. from werkzeug.routing import BaseConverter
  3.  
  4. app = Flask(import_name=__name__)
  5.  
  6. class RegexConverter(BaseConverter):
  7. """
  8. 自定义URL匹配正则表达式
  9. """
  10. def __init__(self, map, regex):
  11. super(RegexConverter, self).__init__(map)
  12. self.regex = regex
  13.  
  14. def to_python(self, value):
  15. """
  16. 路由匹配时,匹配成功后传递给视图函数中参数的值
  17. :param value:
  18. :return:
  19. """
  20. return int(value)
  21.  
  22. def to_url(self, value):
  23. """
  24. 使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数
  25. :param value:
  26. :return:
  27. """
  28. val = super(RegexConverter, self).to_url(value)
  29. return val
  30.  
  31. # 添加到flask中
  32. app.url_map.converters['regex'] = RegexConverter
  33.  
  34. @app.route('/index/<regex("\d+"):nid>')
  35. def index(nid):
  36. print(url_for('index', nid=''))
  37. return 'Index'
  38.  
  39. if __name__ == '__main__':
  40. app.run()

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

四、视图

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

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

  1. #给Flask视图加装饰器
  2. #1、定义1个装饰器
  3.  
  4. def auth(func):
  5. print('我在上面')
  6. def inner(*args,**kwargs):
  7. return func(*args,**kwargs)
  8. return inner
  9.  
  10. app=Flask(__name__)
  11.  
  12. @app.route('/',methods=['GET'])
  13. @auth #注意如果要给视图函数加装饰器,一点要加在路由装饰器下面,才会被路由装饰器装饰
  14. def first_flask():
  15. print('ffff')
  16. return 'Hello World'
  17.  
  18. if __name__ == '__main__':
  19. app.run()

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)

  1. app.config['JSON_AS_ASCII']=False #指定json编码格式 如果为False 就不使用ascii编码,
  2. app.config['JSONIFY_MIMETYPE'] ="application/json;charset=utf-8" #指定浏览器渲染的文件类型,和解码格式;

配置

方式2:

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

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

  1. 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视图

  1. #CBV视图
  2. from flask import Flask,url_for,views
  3. #-----------------------------------------------------
  4. app=Flask(__name__) #装饰器
  5.  
  6. def auth(func):
  7. print('我在上面')
  8. def inner(*args,**kwargs):
  9. return func(*args,**kwargs)
  10. return inner
  11. #--------------------------------------------------------
  12. class IndexView(views.MethodView): #CBV视图
  13. methods=['GET'] #允许的http请求方法(改CBV只允许GET方法)
  14. decorators = [auth,] #每次请求过来都加auth装饰器
  15.  
  16. def get(self):
  17. return 'Index.GET'
  18. def post(self):
  19. return 'Index.POST'
  20.  
  21. app.add_url_rule('/index/',view_func=IndexView.as_view(name='name1')) #(name='name1'反向生成url别名
  22.  
  23. if __name__ == '__main__':
  24. app.run()

CBV视图

 

五、模板语言

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

1.引用静态文件

方式1:别名引入

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

方式2:url_for()方法引入

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

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

变量

  1. <h1>{{user_list}}</h1> <!--变量 -->

循环、索引取值

  1. <ul>
  2. {% for user in user_list %} <!--循环 -->
  3. <li>{{user}}</li>
  4. {% endfor %}
  5.  
  6. {{user_list.0}} <!-- 索引取值-->
  7.  
  8. </ul>

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

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

  1. @app.template_global() #simple_tag
  2. def foo(arg):
  3. return '<input type="text">'

视图

  1. <h1>{{foo(1)|safe}}</h1> <!--Flask的模板语言支持simple_tag-->

模板语言

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

  1. @app.template_filter() #simple_fifter
  2. def foo1(arg1,arg2,arg3):
  3. return arg1+arg2+arg3

视图

  1. <h1> {{ 'alex'|foo1('s ','b',) }} </h1> <!-- simple_fifter -->

模板语言

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

3.0.简介

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

3.1. 安装

  1. pip install wtforms #安装wtfroms插件

3.2.简单使用

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

用户登录页面验证

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3. from flask import Flask, render_template, request, redirect
  4. from wtforms import Form
  5. from wtforms.fields import core
  6. from wtforms.fields import html5
  7. from wtforms.fields import simple
  8. from wtforms import validators
  9. from wtforms import widgets
  10.  
  11. app=Flask(__name__,template_folder='templates') #知道模板文件
  12. app.debug=True
  13.  
  14. #登录验证实例
  15. class LoginForm(Form):
  16.  
  17. #不同的字段 内部包含正则表达式 html5.EmailField | html5.DateTimeField...
  18. name=simple.StringField(
  19. label='用户名',
  20. validators=[ #验证规则和错误提示信息
  21. validators.DataRequired(message='用户名不能为空.'),
  22. validators.Length(min=6, max=18, message='用户名长度必须大于%(min)d且小于%(max)d')
  23. ],
  24. widget=widgets.TextInput(), #前端页面显示的插件.TextArea
  25. render_kw={'class': 'form-control'} #设置form标签的class信息
  26.  
  27. )
  28.  
  29. # 不同的字段 内部包含正则表达式 html5.EmailField | html5.DateTimeField...
  30. pwd = simple.PasswordField(
  31. label='密码',
  32. validators=[
  33. validators.DataRequired(message='密码不能为空.'),
  34. validators.Length(min=8, message='用户名长度必须大于%(min)d'),
  35. #自定义验证规则
  36. validators.Regexp(regex="^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&])[A-Za-z\d$@$!%*?&]{8,}",
  37. message='密码至少8个字符,至少1个大写字母,1个小写字母,1个数字和1个特殊字符')
  38.  
  39. ],
  40. widget=widgets.PasswordInput(),
  41. render_kw={'class': 'form-control'}
  42. )
  43.  
  44. @app.route('/login/', methods=['GET', 'POST'])
  45. def login():
  46. if request.method == 'GET':
  47. form = LoginForm() #实例化 form验证类
  48. return render_template('login.html', form=form)
  49. else:
  50. form = LoginForm(formdata=request.form)
  51. if form.validate(): #判断是否验证成功?
  52. print('用户提交数据通过格式验证,提交的值为:', form.data)
  53. else:
  54. print(form.errors)
  55. return render_template('login.html', form=form)
  56.  
  57. if __name__ == '__main__':
  58. app.run()

app01.py

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <h1>登录</h1>
  9. <form method="post" novalidate>
  10. <!--<input type="text" name="name">-->
  11. <p>{{form.name.label}} {{form.name}} {{form.name.errors[0] }}</p>
  12.  
  13. <!--<input type="password" name="pwd">-->
  14. <p>{{form.pwd.label}} {{form.pwd}} {{form.pwd.errors[0] }}</p>
  15. <input type="submit" value="提交">
  16. </form>
  17. </body>
  18. </html>
  19.  
  20. login.html

login.html

用户注册页面验证

  1. #用户注册
  2. from flask import Flask, render_template, request, redirect
  3. from wtforms import Form
  4. from wtforms.fields import core
  5. from wtforms.fields import html5
  6. from wtforms.fields import simple
  7. from wtforms import validators
  8. from wtforms import widgets
  9.  
  10. app = Flask(__name__, template_folder='templates')
  11. app.debug = True
  12.  
  13. class RegisterForm(Form):
  14. name = simple.StringField(
  15. label='用户名',
  16. validators=[
  17. validators.DataRequired()
  18. ],
  19. widget=widgets.TextInput(),
  20. render_kw={'class': 'form-control'},
  21. default='张根' #设置input标签中默认值
  22. )
  23.  
  24. pwd = simple.PasswordField(
  25. label='密码',
  26. validators=[
  27. validators.DataRequired(message='密码不能为空.')
  28. ],
  29. widget=widgets.PasswordInput(),
  30. render_kw={'class': 'form-control'}
  31. )
  32.  
  33. pwd_confirm = simple.PasswordField( #第二次输入密码
  34. label='重复密码',
  35. validators=[
  36. validators.DataRequired(message='重复密码不能为空.'),
  37. validators.EqualTo('pwd', message="两次密码输入不一致") #验证2次输入的密码是否一致?
  38. ],
  39. widget=widgets.PasswordInput(),
  40. render_kw={'class': 'form-control'}
  41. )
  42.  
  43. email = html5.EmailField(
  44. label='邮箱',
  45. validators=[
  46. validators.DataRequired(message='邮箱不能为空.'),
  47. validators.Email(message='邮箱格式错误')
  48. ],
  49. widget=widgets.TextInput(input_type='email'), #生成email input标签
  50. render_kw={'class': 'form-control'}
  51. )
  52.  
  53. gender = core.RadioField(
  54. label='性别',
  55. choices=( #choice radio选项
  56. (1, '男'),
  57. (2, '女'),
  58. ),
  59. coerce=int #讲用户提交过来的 '4' 强制转成 int 4
  60. )
  61. city = core.SelectField(
  62. label='城市',
  63. choices=(
  64. ('bj', '北京'),
  65. ('sh', '上海'),
  66. )
  67. )
  68.  
  69. hobby = core.SelectMultipleField( #select 下拉框多选框
  70. label='爱好',
  71. choices=(
  72. (1, '篮球'),
  73. (2, '足球'),
  74. ),
  75. coerce=int
  76. )
  77.  
  78. favor = core.SelectMultipleField(
  79. label='喜好',
  80. choices=(
  81. (1, '篮球'),
  82. (2, '足球'),
  83. ),
  84. widget=widgets.ListWidget(prefix_label=False), #生成Checkbox 多选框
  85. option_widget=widgets.CheckboxInput(),
  86. coerce=int,
  87. default=[1, 2]
  88. )
  89.  
  90. def __init__(self, *args, **kwargs): #重写form验证类的__init__方法可以实时同步数据中数据
  91. super(RegisterForm, self).__init__(*args, **kwargs)
  92. self.favor.choices = ((1, '篮球'), (2, '足球'), (3, '羽毛球'))
  93.  
  94. def validate_pwd_confirm(self, field): #wtforms验证 钩子函数
  95. """
  96. 自定义pwd_confirm字段规则,例:与pwd字段是否一致
  97. :param field:
  98. :return:
  99. """
  100. # 最开始初始化时,self.data中已经有所有的值
  101.  
  102. if field.data != self.data['pwd']:
  103. # raise validators.ValidationError("密码不一致") # 继续后续验证
  104. raise validators.StopValidation("密码不一致") # 不再继续后续验证
  105.  
  106. @app.route('/register/', methods=['GET', 'POST'])
  107. def register():
  108. if request.method == 'GET':
  109. form = RegisterForm(data={'gender': 1}) #默认值
  110. return render_template('register.html', form=form)
  111. else:
  112. form = RegisterForm(formdata=request.form)
  113. if form.validate():
  114. print('用户提交数据通过格式验证,提交的值为:', form.data)
  115. else:
  116. print(form.errors)
  117. return render_template('register.html', form=form)
  118.  
  119. if __name__ == '__main__':
  120. app.run()

app02.py

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Title</title>
  6. </head>
  7. <body>
  8. <h1>用户注册</h1>
  9. <form method="post" novalidate style="padding:0 50px">
  10. {% for item in form %}
  11. <p>{{item.label}}: {{item}} {{item.errors[0] }}</p>
  12. {% endfor %}
  13. <input type="submit" value="提交">
  14. </form>
  15. </body>
  16. </html>

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

  1. class InputText(object): #插件
  2. def __str__(self):
  3. return '<input type="text" />'
  4.  
  5. class InputPassword(object):
  6. def __str__(self):
  7. return '<input type="password" />'
  8.  
  9. #-----------------------------------------------------------
  10. class StringField(object): #字段
  11. def __init__(self,wg):
  12. self.widget=wg
  13.  
  14. def __str__(self): #调用插件的__str__
  15. return str(self.widget)
  16.  
  17. class DateField(object):
  18. def __init__(self, wg):
  19. self.widget = wg
  20.  
  21. def __str__(self):
  22. return str(self.widget)
  23.  
  24. #--------------------------------------------------------------
  25. class LoginForm(object): #统一 灵活接口 (对象嵌套对象,多层封装)
  26. name=StringField(wg=InputText()) #wg=InputText() 对象 StringField(wg=InputText())对象
  27. pwd=DateField(wg=InputPassword())
  28.  
  29. l_obj=LoginForm()
  30.  
  31. print(l_obj.name)
  32. print(l_obj.pwd)

调用关系

B.数据校验

后台定义好正则

用户发来数据

对数据进行校验

3.3.源码流程

生成HTML标签并显示

1.验证类(LogibForm)生成

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

  1. '''
  2. class BaseForm():
  3. pass
  4.  
  5. class NewBase(BaseForm,metaclass=FormMeta,):
  6. pass
  7.  
  8. class Form(NewBase):
  9. pass
  10.  
  11. class LoginForm(Form):
  12. pass
  13.  
  14. '''
  15.  
  16. class Form(with_metaclass(FormMeta,BaseForm)):

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

  1. class FormMeta(type):
  2. """
  3. The metaclass for `Form` and any subclasses of `Form`.
  4.  
  5. `FormMeta`'s responsibility is to create the `_unbound_fields` list, which
  6. is a list of `UnboundField` instances sorted by their order of
  7. instantiation. The list is created at the first instantiation of the form.
  8. If any fields are added/removed from the form, the list is cleared to be
  9. re-generated on the next instantiation.
  10.  
  11. Any properties which begin with an underscore or are not `UnboundField`
  12. instances are ignored by the metaclass.
  13. """
  14. def __init__(cls, name, bases, attrs):
  15. type.__init__(cls, name, bases, attrs) #继承type的功能
  16. cls._unbound_fields = None #在LoginForm中添加1个静态字段
  17. cls._wtforms_meta = None #在LoginForm中添加1个静态字段

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

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

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

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

  1.  
  2. def __new__(cls, *args, **kwargs):#执行__new__方法
  3. if '_form' in kwargs and '_name' in kwargs:
  4. return super(Field, cls).__new__(cls)
  5. else:
  6. #我x 没想到 ! __new__既然返回了1个 UnboundField()而不是StringField/PasswordField对象;狸猫换了太子 ?
  7. return UnboundField(cls, *args, **kwargs)

Field.__new__()

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

  1. class UnboundField(object):
  2. _formfield = True
  3. creation_counter = 0 #静态字段 设置计数器
  4.  
  5. def __init__(self, field_class, *args, **kwargs): #field_class=.StringField / PasswordField
  6. #获取到field_class 的 参数封装到 UnboundField对象中,并且设置 排序 'creation_counter': 2
  7.  
  8. UnboundField.creation_counter += 1 #每实例化1个 UnboundField对象 计数器+1
  9. self.field_class = field_class
  10. self.args = args
  11. self.kwargs = kwargs #{'label': '用户名', 'validators': [<wtforms.validators.DataRequired object at 0x00000000038EF080>, <wtforms.validators.Length object at 0x00000000038EF0F0>], 'widget': <wtforms.widgets.core.TextInput object at 0x00000000038EF0B8>, 'render_kw': {'class': 'form-control'}}
  12. self.creation_counter = UnboundField.creation_counter#
  13.  
  14. '''
  15. print(self.__dict__)
  16. {
  17. 'field_class': <class 'wtforms.fields.simple.PasswordField'>,
  18. 'args': (),
  19. 'kwargs': {'label': '密码', 'validators': [<wtforms.validators.DataRequired object at 0x00000000038EF198>, <wtforms.validators.Length object at 0x00000000038EF1D0>, <wtforms.validators.Regexp object at 0x00000000038EF208>], 'widget': <wtforms.widgets.core.PasswordInput object at 0x00000000038EF2B0>,
  20. 'render_kw': {'class': 'form-control'}},
  21. 'creation_counter': 2
  22. }
  23.  
  24. '''

UnboundField.__init__

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

  1. """
  2. print(LoginForm.__dict__)
  3. LoginForm ={
  4. '__module__': '__main__',
  5. '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'} })>,
  6. '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'}})>,
  7. '__doc__': None,
  8. '_unbound_fields': None,
  9. '_wtforms_meta': None,
  10. }
  11. """

启发:

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

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

  1. class Queen(object):
  2. def __new__(cls, *args, **kwargs): #类中__new__方法决定了类(),实例化出什么对象;
  3. return Cat('狸猫','男','太子')
  4.  
  5. def __init__(self,name): #由于__nwe__方法返回了其他对象,所以不会执行Queen的__init__方法
  6. print('ok')
  7. self.name=name
  8.  
  9. Prince=Queen('王子')
  10. print(Prince.name)
  11. print(Prince.gender)
  12. 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属性

  1. def __call__(cls, *args, **kwargs):
  2. if cls._unbound_fields is None: #在创建类时 已经设置LoginForm的_unbound_fields为空
  3. fields = []
  4. # 获取LoginForm类中,中所有属性的key:[ '_get_translations', '_unbound_fields', '_wtforms_met,'name', 'populate_obj', 'process', 'pwd', 'validate'..... ]
  5. for name in dir(cls):
  6. if not name.startswith('_'): #排除__下划线的私有属性 name. pwd
  7. unbound_field = getattr(cls, name) #cls =LoginForm类 #根据key 获取unbound_field 对象
  8. if hasattr(unbound_field, '_formfield'): #检查unbound_field 对象是否包含_formfield = True
  9. fields.append((name, unbound_field))
  10. # '''
  11. # fields = [
  12. # (name,name的unbound对象),
  13. # (pwd,pwd的unbound对象),
  14. # ]
  15. # '''
  16. #对fields 按照定义顺序 进行排序
  17. fields.sort(key=lambda x: (x[1].creation_counter, x[0])) #根据unbound对象的creation_counter进行字段排序
  18. cls._unbound_fields = fields
  19.  
  20. if cls._wtforms_meta is None:
  21. bases = [] #bases = [DefaultMeta],
  22. # 按照继承顺序:获取当前类(FormLogin)所有父类
  23. for mro_class in cls.__mro__:
  24. if 'Meta' in mro_class.__dict__: #去每个父类(mro_class)获取 Meta = DefaultMeta
  25.  
  26. bases.append(mro_class.Meta) #bases = [DefaultMeta],
  27.  
  28. '''
  29. class Meta(DefaultMeta):
  30. pass
  31. '''
  32. cls._wtforms_meta = type('Meta', tuple(bases), {}) #cls._wtforms_meta=Meta(DefaultMeta)类:
  33.  
  34. return type.__call__(cls, *args, **kwargs)

FormMeta.__call__

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

  1. print(LoginForm.__dict__)
  2. LoginForm ={
  3. '__module__': '__main__',
  4. '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'} })>,
  5. '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'}})>,
  6. '__doc__': None,
  7.  
  8. '_unbound_fields': [
  9. (name, UnboundField对象(1simple.StringField,参数)),
  10. (pwd, UnboundField对象(2simple.PasswordField,参数)),
  11. ],,
  12. '_wtforms_meta': Meta(DefaultMeta)类,
  13. }
  14. """

启发:

  1. #sort排序
  2. v1=[
  3. (11,'Martin11',18),
  4. (121,'Martin121',19),
  5. (311,'Martin311',25),
  6. (311, 'Martin311', 26) #按元素1排序,如果元素1相同按照 元素3排序
  7. ]
  8.  
  9. v1.sort(key=lambda x:(x[0],x[2])) #列表的sort方法,根据 列表中的元组元素 进行排序
  10. print(v1)
  11. '''
  12.  
  13. [(11, 'Martin11', 18), (121, 'Martin121', 19), (311, 'Martin311', 25), (311, 'Martin311', 26)]
  14. '''

列表的 sort()

  1. class F1(object):
  2. pass
  3. class F2(object):
  4. pass
  5. class F3(F1):
  6. pass
  7.  
  8. class F4(F2,F3):
  9. pass
  10.  
  11. print(F4.__mro__) #打印F4 的继承关系
  12.  
  13. '''
  14. (
  15. <class '__main__.F4'>,
  16. <class '__main__.F2'>,
  17. <class '__main__.F3'>,
  18. <class '__main__.F1'>,
  19. <class 'object'>)
  20.  
  21. '''

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

2.2.执行LoginForm的__new__方法

没有__new__方法 pass

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

  1. def __init__(self, formdata=None, obj=None, prefix='', data=None, meta=None, **kwargs):
  2.  
  3. # 实例化LoginForm中封装的 Meta类进行实例化,以后用于生成CSRF Tocken 标签
  4. meta_obj = self._wtforms_meta()
  5. #meta是 form = LoginForm(meta={'csrf':'true'})传过来的参数,封装到meta_obj中
  6. if meta is not None and isinstance(meta, dict):
  7. meta_obj.update_values(meta)
  8.  
  9. #执行父类的构造方法,参数
  10. # self._unbound_fields
  11. '''
  12. '_unbound_fields'=[
  13. (name, UnboundField对象(1,simple.StringField,参数)),
  14. (pwd, UnboundField对象(2,simple.PasswordField,参数)),
  15. ],
  16. '''
  17. # meta_ob=Meta(DefaultMeta)对象
  18.  
  19. super(Form, self).__init__(self._unbound_fields, meta=meta_obj, prefix=prefix)
  20. #给 form对象 中的_fields字段赋值如下;
  21. '''
  22. _fields: {
  23. name: StringField对象(),
  24. pwd: PasswordField对象(),
  25. }
  26. name: StringField对象(widget=widgets.TextInput()),
  27. pwd: PasswordField对象(widget=widgets.PasswordInput())
  28.  
  29. '''
  30. #循环form对象 中的_fields字段(字典),给form对象赋值 form.name/form.pwd
  31. for name, field in iteritems(self._fields):
  32. setattr(self, name, field)
  33. '''
  34. _fields: {
  35. name: StringField对象(),
  36. pwd: PasswordField对象(),
  37. }
  38. name: StringField对象(widget=widgets.TextInput()),
  39.  
  40. pwd: PasswordField对象(widget=widgets.PasswordInput())
  41.  
  42. '''
  43. self.process(formdata, obj, data=data, **kwargs)

Form.__init__

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

  1. class BaseForm(object):
  2. def __init__(self, fields, prefix='', meta=DefaultMeta()):
  3. '''
  4. 参数
  5. fields=[
  6. (name, UnboundField对象(1,simple.StringField,参数)),
  7. (pwd, UnboundField对象(2,simple.PasswordField,参数)),
  8. ],
  9.  
  10. meta=Meta(DefaultMeta)对象
  11. '''
  12.  
  13. if prefix and prefix[-1] not in '-_;:/.':
  14. prefix += '-'
  15.  
  16. self.meta = meta
  17. self._prefix = prefix
  18. self._errors = None
  19. self._fields = OrderedDict()
  20.  
  21. if hasattr(fields, 'items'):
  22. fields = fields.items()
  23.  
  24. translations = self._get_translations()
  25. extra_fields = []
  26. #------------------------------------------------------
  27. if meta.csrf: #生成 CSRF tocken隐藏标签
  28. self._csrf = meta.build_csrf(self)
  29. extra_fields.extend(self._csrf.setup_form(self))
  30.  
  31. for name, unbound_field in itertools.chain(fields, extra_fields):#
  32. #(name, UnboundField对象(1,simple.StringField,参数))
  33. #(pwd, UnboundField对象(2,simple.PasswordField,参数))
  34. options = dict(name=name, prefix=prefix, translations=translations)
  35. #(name, UnboundField对象(1,simple.StringField,参数)) #真正实例化 simple.StringField(参数)
  36. field = meta.bind_field(self, unbound_field, options)
  37. #UnboundField对象转换成StringField对象
  38. self._fields[name] = field

BaseForm. __init__

  1. form = {
  2. _fields: {
  3. name: StringField对象(),
  4. pwd: PasswordField对象(),
  5. }
  1.  
  2. 循环form对象 中的_fields字段(字典),分别赋值到form对象,这样就可以通过form.name/form.pwd直接获取到Field对象了
    ,无需form._fields['name'] / form._fields['name']
  1. 代码:
  1. for name, field in iteritems(self._fields):
  2. setattr(self, name, field)
  1.                         form对象封装数据就变成以下内容喽
  1.  
  2. form = {
  3. _fields: {
  4. name: StringField对象(),
  5. pwd: PasswordField对象(),
  6. }
  7. name: StringField对象(widget=widgets.TextInput()),
  8. pwd: PasswordField对象(widget=widgets.PasswordInput())
  9.  
  10. }

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

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

  1. def __str__(self):
  2. return self() #执行LoginForm的__call__方法

Field.__str__方法

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

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

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

  1. def render_field(self, field, render_kw):
  2.  
  3. other_kw = getattr(field, 'render_kw', None)
  4. if other_kw is not None:
  5. render_kw = dict(other_kw, **render_kw)
  6. # StringField对象.widget(field, **render_kw)
  7. #插件.__call__()
  8. '''
  9. #field =StringField对象
  10. StringField对象.widget对象()=调用widget对象的.__call__方法
  11. '''
  12. return field.widget(field, **render_kw)

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

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

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

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

  1. def __iter__(self):
  2. """Iterate form fields in creation order."""
  3. return iter(itervalues(self._fields))
  1. _fields: {
  2. name: StringField对象(),
  3. pwd: PasswordField对象(),
  4. }

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

  1. # 请求发过来的值
  2. form = LoginForm(formdata=request.form) # 值.getlist('name')
  3.  
  4. # 实例:编辑
  5. # # 从数据库对象
  6. # form = LoginForm(obj='值') # 值.name/值.pwd
  7. #
  8. # # 字典 {}
  9. # form = LoginForm(data=request.form) # 值['name']
  10.  
  11. # 1. 循环所有的字段
  12. # 2. 获取每个字段的钩子函数
  13. # 3. 为每个字段执行他的验证流程 字段.validate(钩子函数+内置验证规则)

六、session功能

1. Flask自带的session功能

  1. from flask import session
  2. import json
  3. app=Flask(__name__,template_folder='templates',static_path='/static/',static_url_path='/static/')
  4. app.debug=True
  5. app.secret_key='sjehfjeefrjewth43u' #设置session加密
  6. app.config['JSON_AS_ASCII']=False #指定json编码格式 如果为False 就不使用ascii编码,
  7. app.config['JSONIFY_MIMETYPE'] ="application/json;charset=utf-8" #指定浏览器渲染的文件类型,和解码格式;
  8.  
  9. @app.route('/login/',methods=['GET','POST'])
  10. def login():
  11. msg = ''
  12. if request.method=='POST':
  13. name=request.values.get('user')
  14. pwd=request.values.get('pwd')
  15. if name =='zhanggen' and pwd=='123.com':
  16. session['user']=name #设置session的key value
  17. return redirect('/index/')
  18. else:
  19. msg='用户名或者密码错误'
  20. return render_template('login.html',msg=msg)
  21.  
  22. @app.route('/index/',methods=['GET','POST'])
  23. def index():
  24. user_list = ['张根', 'egon', 'eric']
  25. user=session.get('user') #获取session
  26. if user:
  27. user=['alex','egon','eric']
  28. return jsonify(user_list)
  29. else:
  30. return redirect('/login/')
  31.  
  32. if __name__ == '__main__':
  33. app.run()

Flask自带session功能

2.第三方session组件(Session)

安装 pip install flask-session

  1. from flask import session, Flask,request,make_response,render_template,redirect,jsonify,Response
  2.  
  3. from flask.ext.session import Session #引入第三方session
  4. import json
  5. app=Flask(__name__,template_folder='templates',static_path='/static/',static_url_path='/static/')
  6. app.debug=True
  7. app.secret_key='sjehfjeefrjewth43u' #设置session加密
  8. app.config['JSON_AS_ASCII']=False #指定json编码格式 如果为False 就不使用ascii编码,
  9. app.config['JSONIFY_MIMETYPE'] ="application/json;charset=utf-8" #指定浏览器渲染的文件类型,和解码格式;
  10.  
  11. app.config['SESSION_TYPE']='redis'
  12.  
  13. from redis import Redis #引入连接 redis模块
  14. app.config['SESSION_REDIS']=Redis(host='192.168.0.94',port=6379) #连接redis
  15. Session(app)
  16.  
  17. @app.route('/login/',methods=['GET','POST'])
  18. def login():
  19. msg = ''
  20. if request.method=='POST':
  21. name=request.values.get('user')
  22. pwd=request.values.get('pwd')
  23. if name =='zhanggen' and pwd=='123.com':
  24. session['user']=name #设置session的key value
  25. return redirect('/index/')
  26. else:
  27. msg='用户名或者密码错误'
  28. return render_template('login.html',msg=msg)
  29.  
  30. @app.route('/index/',methods=['GET','POST'])
  31. def index():
  32. user_list = ['张根', 'egon', 'eric']
  33. user=session.get('user') #获取session
  34. if user:
  35. user=['alex','egon','eric']
  36. return jsonify(user_list)
  37. else:
  38. return redirect('/login/')
  39.  
  40. if __name__ == '__main__':
  41. app.run()

把session存到redis

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

  1. def _get_interface(self, app):
  2. config = app.config.copy()
  3. config.setdefault('SESSION_TYPE', 'null')
  4. config.setdefault('SESSION_PERMANENT', True)
  5. config.setdefault('SESSION_USE_SIGNER', False)
  6. config.setdefault('SESSION_KEY_PREFIX', 'session:')
  7. config.setdefault('SESSION_REDIS', None)
  8. config.setdefault('SESSION_MEMCACHED', None)
  9. config.setdefault('SESSION_FILE_DIR',
  10. os.path.join(os.getcwd(), 'flask_session'))
  11. config.setdefault('SESSION_FILE_THRESHOLD', 500)
  12. config.setdefault('SESSION_FILE_MODE', 384)
  13. config.setdefault('SESSION_MONGODB', None)
  14. config.setdefault('SESSION_MONGODB_DB', 'flask_session')
  15. config.setdefault('SESSION_MONGODB_COLLECT', 'sessions')
  16. config.setdefault('SESSION_SQLALCHEMY', None)
  17. config.setdefault('SESSION_SQLALCHEMY_TABLE', 'sessions')
  18.  
  19. if config['SESSION_TYPE'] == 'redis':
  20. session_interface = RedisSessionInterface(
  21. config['SESSION_REDIS'], config['SESSION_KEY_PREFIX'],
  22. config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])
  23. elif config['SESSION_TYPE'] == 'memcached':
  24. session_interface = MemcachedSessionInterface(
  25. config['SESSION_MEMCACHED'], config['SESSION_KEY_PREFIX'],
  26. config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])
  27. elif config['SESSION_TYPE'] == 'filesystem':
  28. session_interface = FileSystemSessionInterface(
  29. config['SESSION_FILE_DIR'], config['SESSION_FILE_THRESHOLD'],
  30. config['SESSION_FILE_MODE'], config['SESSION_KEY_PREFIX'],
  31. config['SESSION_USE_SIGNER'], config['SESSION_PERMANENT'])
  32. elif config['SESSION_TYPE'] == 'mongodb':
  33. session_interface = MongoDBSessionInterface(
  34. config['SESSION_MONGODB'], config['SESSION_MONGODB_DB'],
  35. config['SESSION_MONGODB_COLLECT'],
  36. config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'],
  37. config['SESSION_PERMANENT'])
  38. elif config['SESSION_TYPE'] == 'sqlalchemy':
  39. session_interface = SqlAlchemySessionInterface(
  40. app, config['SESSION_SQLALCHEMY'],
  41. config['SESSION_SQLALCHEMY_TABLE'],
  42. config['SESSION_KEY_PREFIX'], config['SESSION_USE_SIGNER'],
  43. config['SESSION_PERMANENT'])
  44. else:
  45. session_interface = NullSessionInterface()
  46.  
  47. return session_interface

3.自定义session组件

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3. import uuid
  4. import json
  5. from flask.sessions import SessionInterface
  6. from flask.sessions import SessionMixin
  7. from itsdangerous import Signer, BadSignature, want_bytes
  8.  
  9. class MySession(dict, SessionMixin):
  10. def __init__(self, initial=None, sid=None):
  11. self.sid = sid
  12. self.initial = initial
  13. super(MySession, self).__init__(initial or ())
  14.  
  15. def __setitem__(self, key, value):
  16. super(MySession, self).__setitem__(key, value)
  17.  
  18. def __getitem__(self, item):
  19. return super(MySession, self).__getitem__(item)
  20.  
  21. def __delitem__(self, key):
  22. super(MySession, self).__delitem__(key)
  23.  
  24. class MySessionInterface(SessionInterface):
  25. session_class = MySession
  26. container = {}
  27.  
  28. def __init__(self):
  29. import redis
  30. self.redis = redis.Redis()
  31.  
  32. def _generate_sid(self):
  33. return str(uuid.uuid4())
  34.  
  35. def _get_signer(self, app):
  36. if not app.secret_key:
  37. return None
  38. return Signer(app.secret_key, salt='flask-session',
  39. key_derivation='hmac')
  40.  
  41. def open_session(self, app, request):
  42. """
  43. 程序刚启动时执行,需要返回一个session对象
  44. """
  45. sid = request.cookies.get(app.session_cookie_name)
  46. if not sid:
  47. sid = self._generate_sid()
  48. return self.session_class(sid=sid)
  49.  
  50. signer = self._get_signer(app)
  51. try:
  52. sid_as_bytes = signer.unsign(sid)
  53. sid = sid_as_bytes.decode()
  54. except BadSignature:
  55. sid = self._generate_sid()
  56. return self.session_class(sid=sid)
  57.  
  58. # session保存在redis中
  59. # val = self.redis.get(sid)
  60. # session保存在内存中
  61. val = self.container.get(sid)
  62.  
  63. if val is not None:
  64. try:
  65. data = json.loads(val)
  66. return self.session_class(data, sid=sid)
  67. except:
  68. return self.session_class(sid=sid)
  69. return self.session_class(sid=sid)
  70.  
  71. def save_session(self, app, session, response):
  72. """
  73. 程序结束前执行,可以保存session中所有的值
  74. 如:
  75. 保存到resit
  76. 写入到用户cookie
  77. """
  78. domain = self.get_cookie_domain(app)
  79. path = self.get_cookie_path(app)
  80. httponly = self.get_cookie_httponly(app)
  81. secure = self.get_cookie_secure(app)
  82. expires = self.get_expiration_time(app, session)
  83.  
  84. val = json.dumps(dict(session))
  85.  
  86. # session保存在redis中
  87. # self.redis.setex(name=session.sid, value=val, time=app.permanent_session_lifetime)
  88. # session保存在内存中
  89. self.container.setdefault(session.sid, val)
  90.  
  91. session_id = self._get_signer(app).sign(want_bytes(session.sid))
  92.  
  93. response.set_cookie(app.session_cookie_name, session_id,
  94. expires=expires, httponly=httponly,
  95. domain=domain, path=path, secure=secure)

组件

  1. from flask import Flask
  2. from flask import session
  3. from my_session import MySessionInterface
  4.  
  5. app = Flask(__name__)
  6.  
  7. app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'
  8. app.session_interface = MySessionInterface()
  9.  
  10. @app.route('/login/', methods=['GET', "POST"])
  11. def login():
  12. print(session)
  13. session['user1'] = 'alex'
  14. session['user2'] = 'alex'
  15. del session['user2']
  16.  
  17. return "内容"
  18.  
  19. if __name__ == '__main__':
  20. app.run()

应用

 七、蓝图

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

  1. import luffy #导入luffy包就会执行luffy包中__init__.py文件
  2.  
  3. luffy.app.run()

app.py

  1. from flask import Flask
  2.  
  3. app=Flask(__name__,template_folder='templates',static_path='/static/',static_url_path='/static/')
  4.  
  5. app.debug=True
  6.  
  7. from .views import login
  8. from .views import index
  9.  
  10. #把文件中蓝图对象注册到app里
  11. app.register_blueprint(login.login,url_prefix='/login') #访问login蓝图必须以url_prefix开头
  12. app.register_blueprint(index.index,url_prefix='/index')
  13.  
  14. if __name__ == '__main__':
  15. app.run()

luffy包的__init__.py

  1. from flask import Blueprint #导入蓝图
  2. login=Blueprint('login',__name__) #在本模块实例化1个蓝图
  3.  
  4. @login.route('/login/',methods=['GET','POST'])
  5. def login1():
  6. return '登录页面'

login

  1. from flask import Blueprint
  2. index=Blueprint('index',__name__)
  3. @index.route('/index/',methods=['GET','POST'])
  4. def index1():
  5. return '首页'

index

 

八、message (闪现)

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

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

  1. from flask import Flask,request,flash,get_flashed_messages
  2.  
  3. app = Flask(__name__)
  4. app.secret_key = 'some_secret'
  5.  
  6. @app.route('/set/')
  7. def index2():
  8. flash('Disposable') #在message中设置1个个值
  9. return 'ok'
  10.  
  11. #---------------------------------------------------------------------------------
  12.  
  13. @app.route('/')
  14. def index1():
  15. messages = get_flashed_messages() #获取message中设置的值,只能获取1次。(1次性)
  16. print(messages)
  17. return "Index1"
  18.  
  19. if __name__ == "__main__":
  20. app.run()

flask_message

九、中间件

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

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

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

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

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3. from flask import Flask, Request, render_template
  4.  
  5. app = Flask(__name__, template_folder='templates')
  6. app.debug = True
  7.  
  8. @app.before_first_request #第1个请求到来执行
  9. def before_first_request1():
  10. print('before_first_request1')
  11.  
  12. @app.before_request #中间件2
  13. def before_request1():
  14. Request.nnn = 123
  15. print('before_request1') #不能有返回值,一旦有返回值在当前返回
  16.  
  17. @app.before_request
  18. def before_request2():
  19. print('before_request2')
  20.  
  21. @app.errorhandler(404)
  22. def page_not_found(error):
  23. return 'This page does not exist', 404
  24.  
  25. @app.route('/')
  26. def hello_world():
  27. return "Hello World"
  28.  
  29. @app.after_request #中间件 执行视图之后
  30. def after_request1(response):
  31. print('after_request1', response)
  32. return response
  33.  
  34. @app.after_request #中间件 执行视图之后 先执行 after_request2
  35. def after_request2(response):
  36. print('after_request2', response)
  37. return response
  38.  
  39. if __name__ == '__main__':
  40. app.run()

 

十、Flask相关组件

1、flask-sqlchemy

2、flask-script组件

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

  1. pip install flask-script #安装
  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3.  
  4. from sansa import create_app
  5. from flask_script import Manager #导入
  6. app = create_app()
  7.  
  8. manager=Manager(app) #实例化Manager对象
  9.  
  10. if __name__ == '__main__':
  11. manager.run()

run.py

  1.  

python run.py runserver -h 0.0.0.0 -p 8001

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

  1.  

3.flask-migrate组件

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

  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3.  
  4. from sansa import create_app,db
  5. from flask_script import Manager #导入
  6. from flask_migrate import Migrate,MigrateCommand
  7.  
  8. app = create_app()
  9. manager=Manager(app) #实例化Manager对象
  10. migrate=Migrate(app,db)
  11.  
  12. manager.add_command('db',MigrateCommand) #注册命令
  13.  
  14. if __name__ == '__main__':
  15. manager.run()

run.py

  1. 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.

  1. D:\Flask练习\sansa>python run.py db init
  2. Creating directory D:\Flask练习\sansa\migrations ... done
  3. Creating directory D:\Flask练习\sansa\migrations\versions ... done
  4. Generating D:\Flask练习\sansa\migrations\alembic.ini ... done
  5. Generating D:\Flask练习\sansa\migrations\env.py ... done
  6. Generating D:\Flask练习\sansa\migrations\README ... done
  7. Generating D:\Flask练习\sansa\migrations\script.py.mako ... done
  8. Please edit configuration/connection/logging settings in 'D:\\Flask练习\\sansa\\migrations\\alembic.ini' before proceeding.
  9.  
  10. D:\Flask练习\sansa>python run.py db migrate
  11. INFO [alembic.runtime.migration] Context impl MySQLImpl.
  12. INFO [alembic.runtime.migration] Will assume non-transactional DDL.
  13. INFO [alembic.autogenerate.compare] Detected added table 'users666'
  14. Generating D:\Flask练习\sansa\migrations\versions\a7f412a8146f_.py ... done
  15.  
  16. D:\Flask练习\sansa>python run.py db upgrade
  17. INFO [alembic.runtime.migration] Context impl MySQLImpl.
  18. INFO [alembic.runtime.migration] Will assume non-transactional DDL.
  19. INFO [alembic.runtime.migration] Running upgrade -> a7f412a8146f, empty message
  20.  
  21. D:\Flask练习\sansa>

文档:       http://docs.jinkan.org/docs/flask/quickstart.html

银角大王:http://www.cnblogs.com/wupeiqi/articles/7552008.html

银角大王:http://www.cnblogs.com/wupeiqi/articles/5713330.html(执行原生SQL模块 pymsql ,ORM框架 SQLAchemy)

PythonWEB框架之Flask的更多相关文章

  1. python学习笔记:接口开发——PythonWEB框架之Flask

    Flask是一个使用 Python 编写的轻量级 Web 应用框架,安装命令如下 pip install flask 一.服务端接口是怎么开发的? 1.启动一个服务 2.接收到客户端传过来的数据3.登 ...

  2. 15 . PythonWeb框架本质

    PythonWeb框架的本质 简单描述就是:浏览器通过你输入的网址给你的socket服务端发送请求,服务端接受到请求给其回复一个对应的html页面,这就是web项目.所有的Web应用本质上就是一个so ...

  3. python 三大框架之一Flask入门

    Flask轻量级框架,Flask是python中的轻量级框架. 打开终端 输入pip install Flask 命令 下载以及安装Flask框架 检查是否下载成功及能否使用 首先导入python环境 ...

  4. python三大web框架Django,Flask,Flask,Python几种主流框架,13个Python web框架比较,2018年Python web五大主流框架

    Python几种主流框架 从GitHub中整理出的15个最受欢迎的Python开源框架.这些框架包括事件I/O,OLAP,Web开发,高性能网络通信,测试,爬虫等. Django: Python We ...

  5. python框架之Flask

    介绍:Flask是一个使用 Python 编写的轻量级 Web 应用框架.其 WSGI 工具箱采用 Werkzeug ,模板引擎则使用 Jinja2 . WSGl:Web Server Gateway ...

  6. Python-Web框架之 - 利用SQLALchemy创建与数据库MySQL的连接, 详解用Flask时会遇到的一些大坑 !

    经过这个小项目算是对Django与Flask这两个web框架有了新的认识 , Django本身的轮子非常齐全 , 套路也很固定 , 新手在接触Django框架时 , 不会陷入到处找轮子的大坑 ; 那么 ...

  7. pythonweb框架Flask学习笔记05-简单登陆

    源代码从下链接引用:https://www.cnblogs.com/felixwang2/p/9261493.html 我使用的是python3.6 在运行代码的时候遇到了以下问题 session[' ...

  8. pythonweb框架Flask学习笔记04-模板继承

    # -*- coding:utf-8 -*- from flask import render_template,Flask app=Flask(__name__) @app.route('/hell ...

  9. pythonweb框架Flask学习笔记03-变量规则

    #-*- coding:utf-8 -*- from flask import Flask app=Flask(__name__) @app.route('/post/<int:postid&g ...

随机推荐

  1. React native 之设置IOS的图标,名称和启动图(下篇文章会讲到RN的android的相关设置)

    1.首先,app的名称: 如图所示:我的工程名叫BOOk 在BOOk下面的info.plist的文件里设置app的相关信息:比如Bundle name就是设置APP的名称 2.App的图标:(这里注意 ...

  2. js控制的选项卡

    选项卡在各种网站网页上是随处可见的一种形式 今天就简单的讲解下 选项卡得制作方法 首先:思路: 我们做一个四个控制的选项卡  则应该有四个小的DIV 外边包裹着一个大的div 用四个input按钮来控 ...

  3. hdu 1115 Lifting the Stone 多边形的重心

    Lifting the Stone Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others ...

  4. gym 101164 H.Pub crawl 凸包

    题目链接:http://codeforces.com/gym/101164/attachments 题意:对于已知的 n 个二维坐标点,要求按照某种特定的连线方式将尽可能多的点连接(任意相邻的 3 个 ...

  5. 实体entity、JavaBean、Model、POJO、domain的区别

    实体entity.JavaBean.Model.POJO.domain的区别Java Bean.POJO. Entity. VO , 其实都是java 对象,只不过用于不同场合罢了. 按照 Sprin ...

  6. Windows下pipenv将虚环境文件的位置设置在项目根目录下

    在windows下使用pipenv shell时,虚拟环境文件夹会在C:\Users\Administrator\.virtualenvs\目录下默认创建,为了方便管理,将这个虚环境的文件的位置更改一 ...

  7. feature map 大小以及反卷积的理解

    (1)边长的计算公式是:  output_h =(originalSize_h+padding*2-kernelSize_h)/stride +1 输入图片大小为200×200,依次经过一层卷积(ke ...

  8. css3动画实现伪弹幕效果

    如图所示: 效果还可以直接用麦唱APP把一首歌分享到微信里面看到,方法类似全民K歌的方法,都是用css3动画实现的, 代码如下:(这是我做真实效果前的一个dome) 直接粘代码就可以看到效果,里面有两 ...

  9. 不会 tsconfig | tslint 常遇到的问题

    1. require('xx-xx') 不能用时 https://stackoverflow.com/questions/31173738/typescript-getting-error-ts230 ...

  10. WAMP环境配置

    mysql 5.7.12 php 7.1 apache 24 windows 10 64bit 下载好代码后, 1.首先把更目录的sql.sql导入到数据库2.打开application\config ...