flask_内容介绍
Flask简单使用
from flask import Flask 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
一、flask参数说明
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)
配置文件 app.config.from_object('settings.DevelopmentConfig')
flask中的配置文件是一个flask.config.Config对象(继承字典),默认配置为:
{
'DEBUG': False, # 是否开启Debug模式
'TESTING': False, # 是否开启测试模式
'PROPAGATE_EXCEPTIONS': None, # 异常传播(是否在控制台打印LOG) 当Debug或者testing开启后,自动为True
'PRESERVE_CONTEXT_ON_EXCEPTION': None, # 一两句话说不清楚,一般不用它
'SECRET_KEY': None, # 之前遇到过,在启用Session的时候,一定要有它
'PERMANENT_SESSION_LIFETIME': 31, # days , Session的生命周期(天)默认31天
'USE_X_SENDFILE': False, # 是否弃用 x_sendfile
'LOGGER_NAME': None, # 日志记录器的名称
'LOGGER_HANDLER_POLICY': 'always',
'SERVER_NAME': None, # 服务访问域名
'APPLICATION_ROOT': None, # 项目的完整路径
'SESSION_COOKIE_NAME': 'session', # 在cookies中存放session加密字符串的名字
'SESSION_COOKIE_DOMAIN': None, # 在哪个域名下会产生session记录在cookies中
'SESSION_COOKIE_PATH': None, # cookies的路径
'SESSION_COOKIE_HTTPONLY': True, # 控制 cookie 是否应被设置 httponly 的标志,
'SESSION_COOKIE_SECURE': False, # 控制 cookie 是否应被设置安全标志
'SESSION_REFRESH_EACH_REQUEST': True, # 这个标志控制永久会话如何刷新
'MAX_CONTENT_LENGTH': None, # 如果设置为字节数, Flask 会拒绝内容长度大于此值的请求进入,并返回一个 413 状态码
'SEND_FILE_MAX_AGE_DEFAULT': 12, # hours 默认缓存控制的最大期限
'TRAP_BAD_REQUEST_ERRORS': False,
# 如果这个值被设置为 True ,Flask不会执行 HTTP 异常的错误处理,而是像对待其它异常一样,
# 通过异常栈让它冒泡地抛出。这对于需要找出 HTTP 异常源头的可怕调试情形是有用的。
'TRAP_HTTP_EXCEPTIONS': False,
# Werkzeug 处理请求中的特定数据的内部数据结构会抛出同样也是“错误的请求”异常的特殊的 key errors 。
# 同样地,为了保持一致,许多操作可以显式地抛出 BadRequest 异常。
# 因为在调试中,你希望准确地找出异常的原因,这个设置用于在这些情形下调试。
# 如果这个值被设置为 True ,你只会得到常规的回溯。
'EXPLAIN_TEMPLATE_LOADING': False,
'PREFERRED_URL_SCHEME': 'http', # 生成URL的时候如果没有可用的 URL 模式话将使用这个值
'JSON_AS_ASCII': True,
# 默认情况下 Flask 使用 ascii 编码来序列化对象。如果这个值被设置为 False ,
# Flask不会将其编码为 ASCII,并且按原样输出,返回它的 unicode 字符串。
# 比如 jsonfiy 会自动地采用 utf-8 来编码它然后才进行传输。
'JSON_SORT_KEYS': True,
#默认情况下 Flask 按照 JSON 对象的键的顺序来序来序列化它。
# 这样做是为了确保键的顺序不会受到字典的哈希种子的影响,从而返回的值每次都是一致的,不会造成无用的额外 HTTP 缓存。
# 你可以通过修改这个配置的值来覆盖默认的操作。但这是不被推荐的做法因为这个默认的行为可能会给你在性能的代价上带来改善。
'JSONIFY_PRETTYPRINT_REGULAR': True,
'JSONIFY_MIMETYPE': 'application/json',
'TEMPLATES_AUTO_RELOAD': None,
}
}
app.config.from_pyfile("python文件名称") 如: settings.py DEBUG = True app.config.from_pyfile("settings.py") app.config.from_envvar("环境变量名称") 环境变量的值为python文件名称名称,内部调用from_pyfile方法 app.config.from_json("json文件名称") JSON文件名称,必须是json格式,因为内部会执行json.loads app.config.from_mapping({'DEBUG':True}) 字典格式 app.config.from_object("python类或类的路径") app.config.from_object('pro_flask.settings.TestingConfig') settings.py class Config(object): DEBUG = False TESTING = False DATABASE_URI = 'sqlite://:memory:' class ProductionConfig(Config): DATABASE_URI = 'mysql://user@localhost/foo' class DevelopmentConfig(Config): DEBUG = True class TestingConfig(Config): TESTING = True PS: 从sys.path中已经存在路径开始写 PS: settings.py文件默认路径要放在程序root_path目录,如果instance_relative_config为True,则就是instance_path目录
方式一: app.config['DEBUG'] = True PS: 由于Config对象本质上是字典,所以还可以使用app.config.update(...) 方式二: app.config.from_pyfile("python文件名称") 如: settings.py DEBUG = True app.config.from_pyfile("settings.py") app.config.from_envvar("环境变量名称") 环境变量的值为python文件名称名称,内部调用from_pyfile方法 app.config.from_json("json文件名称") JSON文件名称,必须是json格式,因为内部会执行json.loads app.config.from_mapping({'DEBUG':True}) 字典格式 app.config.from_object("python类或类的路径") app.config.from_object('pro_flask.settings.TestingConfig') settings.py class Config(object): DEBUG = False TESTING = False DATABASE_URI = 'sqlite://:memory:' class ProductionConfig(Config): DATABASE_URI = 'mysql://user@localhost/foo' class DevelopmentConfig(Config): DEBUG = True class TestingConfig(Config): TESTING = True PS: 从sys.path中已经存在路径开始写 PS: settings.py文件默认路径要放在程序root_path目录,如果instance_relative_config为True,则就是instance_path目录
二、路由系统
1.动态路由(url传参)
@app.route('/user/<name>')
from flask import Flask app=Flask(__name__) @app.route('/<name>') #设置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/<int:age>')
#接收整型数字参数 app=Flask(__name__) @app.route('/<int:age>/') #设置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/<float:salary>')
#接收浮点型型数字参数 app=Flask(__name__) @app.route('/<float:salary>/') #设置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/<path:path>')
# 接收URL链接类型参数 app=Flask(__name__) @app.route('/<path:url>/') #设置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()
2、指定允许的请求方法
@app.route('/login', methods=['GET', 'POST'])
# 指定允许的请求方法 app=Flask(__name__) @app.route('/<path:url>/',methods=['get']) #只允许get请求 def first_flask(url): print(url) return 'Hello World' #response if __name__ == '__main__': app.run()
3、通过别名反向生成url
#反向生成url from flask import Flask,url_for app=Flask(__name__) @app.route('/<path:url>',endpoint='name1') def first_flask(url): print(url_for('name1',url=url)) #如果设置了url参数,url_for(别名,加参数) return 'Hello World' if __name__ == '__main__': app.run()
4、路由调用方式二:通过app.add_url_rule()调用路由
#方式2通过app.add_url_rule()方法的方式调用路由 app=Flask(__name__) def first_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(
5、扩展路由功能:正则匹配url
如果需要一些复杂的匹配规则可以自定义正则匹配url
from flask import Flask, views, url_for from werkzeug.routing import BaseConverter app = Flask(import_name=__name__) class RegexConverter(BaseConverter): """ 自定义URL匹配正则表达式 """ def __init__(self, map, regex): super(RegexConverter, self).__init__(map) self.regex = regex def to_python(self, value): """ 路由匹配时,匹配成功后传递给视图函数中参数的值 :param value: :return: """ return int(value) def to_url(self, value): """ 使用url_for反向生成URL时,传递的参数经过该方法处理,返回的值用于生成URL中的参数 :param value: :return: """ val = super(RegexConverter, self).to_url(value) return val # 添加到flask中 app.url_map.converters['regex'] = RegexConverter @app.route('/index/<regex("\d+"):nid>') def index(nid): print(url_for('index', nid='888')) return 'Index' if __name__ == '__main__': app.run()
三、特殊装饰器
模板相关的装饰器
@app.template_global()
用法:
@app.template_global() # 记得加括号 def jiafa(a, b): # 这个方法每调用一次就需要传一次, 将他做成一个全局的就用这么麻烦了 return int(a) + int(b)
在模板中这样使用
<h2>{{ jiafa(100, 30)}}</h2>
可以在全局范围的模板中使用这个函数, 而不用通过参数传到模板中
@app.template_filter()
用法:
@app.template_filter() def jianfa(a, b, c): return a - b - c
在模板中这样使用
<h2>{{ 100|jianfa(100, 300) }}</h2>
类似于django中间件的装饰器
@app.before_request
用法:
@app.before_request def confirm(): """ 在执行视图函数之前执行 :return: """ if session.get('auth') or request.path == "/login" or request.path.startswith('/static'): return None else: next_url = request.path return redirect('/login?next=%s' % (next_url,))
@app.after_request
用法:
@app.after_request def access_login(response): """ 视图函数执行完毕并执行成功后执行 :param response: 必须接受一个response :return: """ ip = request.remote_addr url = request.path print("%s访问 %s 成功" % (ip, url)) return response # 必须返回一个response对象
@app.errorhandler(404)
@app.errorhandler(404) # 404: 监听的错误码 def error(args): """ 当出现某个错误状态码时会调用这个函数 :param args: 错误信息 :return: """ return render_template("error.html")
@app.before_first_request
第一次请求响应之前执行被装饰的函数
@app.before_first_request def before_first_request1(): print('before_first_request1')
不同装饰器的执行的顺序
多个被装饰器装饰的函数的执行顺序是什么呢?
@app.before_first_request: 按照代码顺序, 从上往下执行
@app.before_request: 按照代码顺序, 从上往下执行
@app.after_request: 按照代码顺序, 从下往上执行
@app.errorhandler(404): 监听到哪一个错误码就执行对应的函数
四、视图
1、给Flask视图函数加装饰器
注意如果要给视图函数加装饰器增加新功能,一点要加在路由装饰器下面,才会被路由装饰器装饰,才能生生成url关系
#给Flask视图加装饰器 #1、定义1个装饰器 def auth(func): print('我在上面') def inner(*args,**kwargs): return func(*args,**kwargs) return inner app=Flask(__name__) @app.route('/',methods=['GET']) @auth #注意如果要给视图函数加装饰器,一点要加在路由装饰器下面,才会被路由装饰器装饰 def first_flask(): print('ffff') return 'Hello World' if __name__ == '__main__': 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)
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 import Flask,url_for,views #----------------------------------------------------- app=Flask(__name__) #装饰器 def auth(func): print('我在上面') def inner(*args,**kwargs): return func(*args,**kwargs) return inner #-------------------------------------------------------- class IndexView(views.MethodView): #CBV视图 methods=['GET'] #允许的http请求方法(改CBV只允许GET方法) decorators = [auth,] #每次请求过来都加auth装饰器 def get(self): return 'Index.GET' def post(self): return 'Index.POST' app.add_url_rule('/index/',view_func=IndexView.as_view(name='name1')) #(name='name1'反向生成url别名 if __name__ == '__main__': app.run()
五、模板语言
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.模板语言引用上下文对象
变量
<h1>{{user_list}}</h1> <!--变量 -->
循环、索引取值
<ul> {% for user in user_list %} <!--循环 --> <li>{{user}}</li> {% endfor %} {{user_list.0}} <!-- 索引取值--> </ul>
Flask的Jinjia2可以通过Context 把视图中的函数传递把模板语言中执行,这就是Django中的simple_tag和simple_fifter;
simple_tag(只能传2个参数,支持for、if)
@app.template_global() #simple_tag def foo(arg): return '<input type="text">'
<h1>{{foo(1)|safe}}</h1> <!--Flask的模板语言支持simple_tag-->
simple_fifter(对参数个数无限制,不支持for、if)
@app.template_filter() #simple_fifter def foo1(arg1,arg2,arg3): return arg1+arg2+arg3
<h1> {{ 'alex'|foo1('s ','b',) }} </h1> <!-- simple_fifter -->
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 import Flask, render_template, request, redirect from wtforms import Form from wtforms.fields import core from wtforms.fields import html5 from wtforms.fields import simple from wtforms import validators from wtforms import widgets app=Flask(__name__,template_folder='templates') #知道模板文件 app.debug=True #登录验证实例 class LoginForm(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']) def login(): 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()
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>登录</h1> <form method="post" novalidate> <!--<input type="text" name="name">--> <p>{{form.name.label}} {{form.name}} {{form.name.errors[0] }}</p> <!--<input type="password" name="pwd">--> <p>{{form.pwd.label}} {{form.pwd}} {{form.pwd.errors[0] }}</p> <input type="submit" value="提交"> </form> </body> </html>
用户注册页面验证
#用户注册 from flask import Flask, render_template, request, redirect from wtforms import Form from wtforms.fields import core from wtforms.fields import html5 from wtforms.fields import simple from wtforms import validators from wtforms import widgets app = Flask(__name__, template_folder='templates') app.debug = True class RegisterForm(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']) def register(): if request.method == 'GET': form = RegisterForm(data={'gender': 1}) #默认值 return render_template('register.html', form=form) else: form = RegisterForm(formdata=request.form) if form.validate(): print('用户提交数据通过格式验证,提交的值为:', form.data) else: print(form.errors) return render_template('register.html', form=form) if __name__ == '__main__': app.run()
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>用户注册</h1> <form method="post" novalidate style="padding:0 50px"> {% for item in form %} <p>{{item.label}}: {{item}} {{item.errors[0] }}</p> {% endfor %} <input type="submit" value="提交"> </form> </body> </html>
六、session功能
1. Flask自带的session功能
from flask import session import json 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']) def login(): 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']) def index(): user_list = ['张根', 'egon', 'eric'] user=session.get('user') #获取session if user: user=['alex','egon','eric'] return jsonify(user_list) else: return redirect('/login/') if __name__ == '__main__': app.run()
2.第三方session组件(Session)
安装 pip install flask-session
from flask import session, Flask,request,make_response,render_template,redirect,jsonify,Response from flask.ext.session import Session #引入第三方session import json 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']) def login(): 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']) def index(): user_list = ['张根', 'egon', 'eric'] user=session.get('user') #获取session if user: user=['alex','egon','eric'] return jsonify(user_list) else: return redirect('/login/') if __name__ == '__main__': app.run()
不仅可以把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
3.自定义session组件
#!/usr/bin/env python # -*- coding:utf-8 -*- import uuid import json from flask.sessions import SessionInterface from flask.sessions import SessionMixin from itsdangerous import Signer, BadSignature, want_bytes class MySession(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) class MySessionInterface(SessionInterface): session_class = MySession container = {} def __init__(self): import redis self.redis = redis.Redis() def _generate_sid(self): return str(uuid.uuid4()) def _get_signer(self, app): if not app.secret_key: return None return Signer(app.secret_key, salt='flask-session', key_derivation='hmac') def open_session(self, app, request): """ 程序刚启动时执行,需要返回一个session对象 """ sid = request.cookies.get(app.session_cookie_name) if not sid: 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() except BadSignature: 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 not None: 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) def save_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 import Flask from flask import session from my_session import MySessionInterface app = Flask(__name__) app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT' app.session_interface = MySessionInterface() @app.route('/login/', methods=['GET', "POST"]) def login(): 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()
from flask import Flask app=Flask(__name__,template_folder='templates',static_path='/static/',static_url_path='/static/') app.debug=True from .views import login from .views import index #把文件中蓝图对象注册到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()
from flask import Blueprint #导入蓝图 login=Blueprint('login',__name__) #在本模块实例化1个蓝图 @login.route('/login/',methods=['GET','POST']) def login1(): return '登录页面'
from flask import Blueprint index=Blueprint('index',__name__) @index.route('/index/',methods=['GET','POST']) def index1(): return '首页'
八、message (闪现)
message是一个基于Session实现的用于保存数据的集合,其特点是:一次性。
特点:和labada匿名函数一样不长期占用内存
from flask import Flask,request,flash,get_flashed_messages app = Flask(__name__) app.secret_key = 'some_secret' @app.route('/set/') def index2(): flash('Disposable') #在message中设置1个个值 return 'ok' #--------------------------------------------------------------------------------- @app.route('/') def index1(): messages = get_flashed_messages() #获取message中设置的值,只能获取1次。(1次性) print(messages) return "Index1" if __name__ == "__main__": app.run()
九、中间件
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 import Flask, Request, render_template app = Flask(__name__, template_folder='templates') app.debug = True @app.before_first_request #第1个请求到来执行 def before_first_request1(): print('before_first_request1') @app.before_request #中间件2 def before_request1(): Request.nnn = 123 print('before_request1') #不能有返回值,一旦有返回值在当前返回 @app.before_request def before_request2(): print('before_request2') @app.errorhandler(404) def page_not_found(error): return 'This page does not exist', 404 @app.route('/') def hello_world(): return "Hello World" @app.after_request #中间件 执行视图之后 def after_request1(response): print('after_request1', response) return response @app.after_request #中间件 执行视图之后 先执行 after_request2 def after_request2(response): print('after_request2', response) return response if __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 import create_app from flask_script import Manager #导入 app = create_app() manager=Manager(app) #实例化Manager对象 if __name__ == '__main__': manager.run()
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 import create_app,db from flask_script import Manager #导入 from flask_migrate import Migrate,MigrateCommand app = create_app() manager=Manager(app) #实例化Manager对象 migrate=Migrate(app,db) manager.add_command('db',MigrateCommand) #注册命令 if __name__ == '__main__': manager.run()
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
连接池
from datetime import timedelta from redis import Redis import pymysql from DBUtils.PooledDB import PooledDB, SharedDBConnection class Config(object): DEBUG = True SECRET_KEY = "umsuldfsdflskjdf" PERMANENT_SESSION_LIFETIME = timedelta(minutes=20) SESSION_REFRESH_EACH_REQUEST= True SESSION_TYPE = "redis" PYMYSQL_POOL = PooledDB( creator=pymysql, # 使用链接数据库的模块 maxconnections=6, # 连接池允许的最大连接数,0和None表示不限制连接数 mincached=2, # 初始化时,链接池中至少创建的空闲的链接,0表示不创建 maxcached=5, # 链接池中最多闲置的链接,0和None不限制 maxshared=3, # 链接池中最多共享的链接数量,0和None表示全部共享。PS: 无用,因为pymysql和MySQLdb等模块的 threadsafety都为1,所有值无论设置为多少,_maxcached永远为0,所以永远是所有链接都共享。 blocking=True, # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错 maxusage=None, # 一个链接最多被重复使用的次数,None表示无限制 setsession=[], # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."] ping=0, # ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always host='127.0.0.1', port=3306, user='root', password='123456', database='s8day127db', charset='utf8' ) class ProductionConfig(Config): SESSION_REDIS = Redis(host='192.168.0.94', port='6379') class DevelopmentConfig(Config): SESSION_REDIS = Redis(host='127.0.0.1', port='6379') class TestingConfig(Config): pass
SQLAlchemy
文档: 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)