1. 关于session

flask session可能很多人根本都没有使用过,倒是cookie大家可能使用得比较多。flask cookie使用起来比较简单,就两个函数,读取和设置。

具体使用方式如下:

读取cookie

from flask import request

@app.route('/')
def index():
username = request.cookies.get('username')
# 使用 cookies.get(key) 来代替 cookies[key] ,
# 以避免当 cookie 不存在时引发 KeyError 。

设置cookie

@app.route('/')
def index():
resp = make_response(render_template(...))
resp.set_cookie('username', 'the username')
return resp

cookie的设置是必须在response返回时,并且是作为response的一部分给client段返回的。

其实理解了cookie的原理就大概知道为什么这么做了,cookie是为了保存用户的一些信息,而这些信息其实是保存在浏览器的缓存中的,大家平时清理浏览器的访问记录时其实就是在清除对应的cookie。

每次请求的时候,浏览器会根据所访问的网页把对应保存的用户相关的cookie信息放到request中给服务端发送过去。这样就节省了多次身份认证的过程。

关于flask cookie的详细使用方法可以参阅:http://dormousehole.readthedocs.io/en/latest/quickstart.html?highlight=cookie。这块就不细讲了。

这节主要讲的是session,具体cookie和session的区别,在网上可以查找到很多相关的资料。其实很多事情不需要刻意理解他们之间的具体差别,实际中遇到就能很好地理解他们之间的差别。

本文不会具体讲cookie和session概念上的差别,不过通过介绍完我使用的场景,想必大家就很清楚他们之间的区别了。

2 遇到的问题

自动化发布平台在用户创建工单表单的时候,需要用户指定其待发布的机器列表。这样OP在具体发布的时候根据发布所处的阶段来选择具体发布哪几台机器。因此,需要根据用户对应的业务从配置服务中拉取对应的机器列表让其选择。

并且这个机器列表是可能变化的。所以,这块单纯使用wtforms.fields.SelectField表单字段是不起作用的,因此需要使用wtforms.ext.sqlalchemy.fields的QuerySelectField字段。至于QuerySelectField的使用可以参考我的

上篇博客。

OP在发布的不同阶段,需要选定机器列表做具体的发布。具体机器列表的获取,也可以使用和创建工单的时候同样的方式,从配置服务中获取当前业务所有的机器列表。然后,让发布者从机器列表中选择机器列表中选择几台具体的机器做发布。

但是,这块会有一个问题。创建工单的时候,需要创建者从一个机器列表中选择几台期待的机器,而发布的各个阶段也都需要从同样的机器列表中选择几台机器做发布。这样就可能存在一个问题,发布者发布的时候应该是从创建工单的人选择的

机器列表中选择机器做发布,而不是从全局的机器列表中选择机器做发布。

因此,需要有种方式在创建表单的时候能够把工单的ID传递过去,这样可以从数据库中把工单的详细信息查询出来,并获取到用户选定的机器列表。

但是,这块用什么方式传递过去呢?

可能这样说不是很形象,具体用代码展示吧,发布过程中有预发布、灰度发布、以及发布。但是,这块以预发布为例,灰度发布和发布其实都一样。

    @expose('/pre_release', methods = ['GET', 'POST'])
def pre_release(self):
pre_release_handle_form = PreReleaseHandleForm(request.form)
if request.method == 'POST':
if helpers.validate_form_on_submit(pre_release_handle_form):
# user logic
return self.render('release.html', form = pre_release_handle_form

上面是预发布相关的逻辑,核心逻辑都去掉了,只留个大致的框架,不过这并不影响本文介绍的内容。

而PreReleaseHandleForm表结构如下:

class PreReleaseHandleForm(form.Form):
def query_factory():
return [r.task_name for r in db.session.query(Task).all()] def get_pk(obj):
return obj def iplist_query_factory():
# 从配置服务中获取
iplist = get_from_config_server()
return iplist def iplist_get_pk(obj):
return obj release_task = QuerySelectField(label=u'发布任务',
validators=[validators.required()],
query_factory=query_factory,
get_pk=get_pk) rollback_task = QuerySelectField(label=u'回滚任务',
validators=[validators.required()],
query_factory=query_factory,
get_pk=get_pk) target_machine = QuerySelectMultipleField(label=u'目标机器',
validators = [validators.required()],
query_factory=iplist_query_factory,
get_pk=iplist_get_pk) remark = fields.TextField(label=u'备注', validators=[validators.required()])

此处可以看到QuerySelectMultipleField函数,其能够支持表单的多选以及能够动态获取选项列表。因此,在此处替代MultiSelectField。

上述的代码不做详细介绍,算是比较简单的使用,我们关注的核心是iplist_query_factory函数,它是本文的核心。预发布的机器列表就是由此函数产生的。

但是,由于我们在预发布的时候表单并没有上下文,我们不知道其对应哪个具体的工单,而且表单肯定是无状态的。所以,就跟此处有冲突,我们期待在表单页面显示的时候就能够

根据具体的上下文从创建的工单中获取待发布的机器列表,并展示出来。

3 解决的办法

因此,第一个想法是使用cookie,这样我们在向客户端显示表单页面的时候顺便把工单ID放个response中并给客户端返回。这样iplist_query_factory函数会根据request上下文从cookie中

取出对应的ID,并从数据库中查询对应的工单详细信息,并回去对应的待发布机器列表并给客户端展示。

不过这块需要PreReleaseHandleFormpre_release函数都做稍微的修改,具体修改内容如下:

def iplist_query_factory():
id = request.cookie.get('ID')
job = db.session.query(Job).filter_by(id = id).first()
iplist = job.pre_release_iplist.split(',')
return iplist

  

    @expose('/pre_release', methods = ['GET', 'POST'])
def pre_release(self):
id = request.args.get('id', '')
pre_release_handle_form = PreReleaseHandleForm(request.form)
if request.method == 'POST':
if helpers.validate_form_on_submit(pre_release_handle_form):
# user logic resp = make_response(self.render('release.html', form = pre_release_handle_form))
resp.set_cookie('ID', id)
return resp

具体的话,大家可以关注上面加粗的地方。其实还是蛮容易理解的,在向客户端返回response的时候需要在response的cookie属性中设置对应的ID字段的值,在iplist_query_factory中从cookie中查询对应字段的值。

这块并不会因为多线程而出现问题,因为在flask中request上下文是线程安全的。

但是测试的时候发现这块有个问题,就是我第一次展示表单页面的时候IP列表是空的,必须我重新刷新一次才可以。之后的情况就是我重新打开页面,显示的机器列表确实上一次的机器列表。

后来想了想,才发现一个问题。每次的表单显示的时候其实对应的response并没有给客户端返回,因此拿到的是上次请求的cookie值,这样就存在着延后,跟真实的情况不同步。

要是任由这种情况发展还是蛮危险的。因此,只能使用另外一种方式解决了,后来发现另外一种解决方式就是session。

看了下flask admin session的具体实现,发现flask session其实是request上下文,而flask的实现其实对于每个request会分派给一个独立的线程,其实是线程安全的。

from functools import partial
from werkzeug.local import LocalStack, LocalProxy def _lookup_req_object(name):
top = _request_ctx_stack.top
if top is None:
raise RuntimeError('working outside of request context')
return getattr(top, name) def _lookup_app_object(name):
top = _app_ctx_stack.top
if top is None:
raise RuntimeError('working outside of application context')
return getattr(top, name) def _find_app():
top = _app_ctx_stack.top
if top is None:
raise RuntimeError('working outside of application context')
return top.app # context locals
_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()
current_app = LocalProxy(_find_app)
request = LocalProxy(partial(_lookup_req_object, 'request'))
session = LocalProxy(partial(_lookup_req_object, 'session'))
g = LocalProxy(partial(_lookup_app_object, 'g'))

上面是flask源代码,具体只需要看下上面加粗的代码就可以了。可以看出session其实是request上下文安全的。

因此,可以直接使用。至于fllask session的使用也是蛮简单的。具体代码如下:

 def iplist_query_factory():
id = session['ID']
job = db.session.query(Job).filter_by(id = id).first()
iplist = job.pre_release_iplist.split(',')
return iplist

  

@expose('/pre_release', methods = ['GET', 'POST'])
def pre_release(self):
id = request.args.get('id', '')
session['ID'] = id
pre_release_handle_form = PreReleaseHandleForm(request.form)
if request.method == 'POST':
if helpers.validate_form_on_submit(pre_release_handle_form):
# user logic returnn self.render('release.html', form = pre_release_handle_form)

上面加粗的代码就是session的使用,发现没有其实session使用起来比cookie更方便。

测试发现效果完全符合预期,但是蛮不错的。

4 小结

之前关于这个问题想了好久,没有想到具体的解决方案,后来无意间想到了。所以,遇到问题不要放弃,坚持就是胜利!

flask-admin章节四:flask session的使用的更多相关文章

  1. 初识Flask框架,以及Flask中的模板语言jinjia2和Flask内置的Session

    一.web框架的对比 首先我们先来看下比较火的web框架 1.Django: 优点:大而全,所有组件都是组织内部开发高度定制化,教科书级别的框架 缺点:大到浪费资源,请求的时候需要的资源较高 2.Fl ...

  2. Flask 教程 第四章:数据库

    本文翻译自 The Flask Mega-Tutorial Part IV: Database 在Flask Mega-Tutorial系列的第四部分,我将告诉你如何使用数据库. 本章的主题是重中之重 ...

  3. Flask框架 (四)—— 请求上下文源码分析、g对象、第三方插件(flask_session、flask_script、wtforms)、信号

    Flask框架 (四)—— 请求上下文源码分析.g对象.第三方插件(flask_session.flask_script.wtforms).信号 目录 请求上下文源码分析.g对象.第三方插件(flas ...

  4. [Python自学] Flask框架 (1) (Flask介绍、配置、Session、路由、请求和响应、Jinjia2模板语言、视图装饰器)

    oldboy:s9day114 参考博客:https://www.cnblogs.com/wupeiqi/articles/7552008.html 一.Flask简介 1.安装Flask pip i ...

  5. Flask中使用cookie和session

    Flask中使用cookie和session 设置cookie from flask import Flask,Response app = Flask(__name__) @app.route('/ ...

  6. Flask 学习(四)静态文件

    Flask 学习(四)静态文件 动态 web 应用也需要静态文件,一般是 CSS 和 JavaScript 文件.理想情况下你的服务器已经配置好提供静态文件的服务. 在开发过程中, Flask 也能做 ...

  7. flask admin学习记录

    flask admin是flask框架中一个非常好用的后台管理框架,但是由于文档内容太少,经常遇到问题无法解决,这里记录一下 一简单的使用 from flask import Flask from f ...

  8. flask框架基本使用(3)(session与cookies)

    #转载请留言联系 flask 框架基本使用(1):https://www.cnblogs.com/chichung/p/9756935.html flask 框架基本使用(2):https://www ...

  9. Flask框架(五) —— session源码分析

    Flask框架(五) —— session源码分析 目录 session源码分析 1.请求来了,执行__call__方法 2.__call__方法 3.调用__call__方法 3.1.ctx = s ...

随机推荐

  1. C语言PIC32 serial bootloader和C#语言bootloader PC端串口通信程序

    了解更多关于bootloader 的C语言实现,请加我QQ: 1273623966 (验证信息请填 bootloader),欢迎咨询或定制bootloader(在线升级程序). 今天介绍下我新完成的为 ...

  2. 14.S5PV210串行通信编程实战

    1.整个程序流程分析(1)整个串口通信相关程序包含2部分:uart_init负责初始化串口,uart_putc负责发送一个字节2.串口控制器初始化关键步骤(1)初始化串口的Tx和Rx引脚所对应的GPI ...

  3. 浅述python中argsort()函数的用法

    由于想使用python用训练好的caffemodel来对很多图片进行批处理分类,学习过程中,碰到了argsort函数,因此去查了相关文献,也自己在python环境下进行了测试,大概了解了其相关的用处, ...

  4. TripleDES加解密Java、C#、php通用代码

    TripleDES说明:     TripleDES(3Des)和Des都是对称加密算法,TripleDes是Des加密算法的增强版本,这里主要说的是TripleDes加密算法的应用.     工作中 ...

  5. 【232】◀▶ IDL显示地理图像

    参考: 01   IMAGE 将图像数据以图形窗体的形式显示. 02   COLORBAR 在已经存在的IDL图形中增加一个colorbar或创建. 03   MAPGRID 在已经存在的IDL地图图 ...

  6. Kafka深入理解-1:Kafka高效的文件存储设计

    文章摘自:美团点评技术团队  Kafka文件存储机制那些事 Kafka是什么 Kafka是最初由Linkedin公司开发,是一个分布式.分区的.多副本的.多订阅者,基于zookeeper协调的分布式日 ...

  7. 转:永久解决火狐浏览器出现的flash版本更新问题

    发现问题: 1.找到火狐浏览器,并打开出现问题的页面. 2.点击火狐浏览器的"打开菜单"按钮,选择"附加组件"按钮. 3.在"搜索所有添加附件&quo ...

  8. HTML5标签的3大类型

    1>块级标签: 独占一行的标签,能随时设置宽度和高度 比如:div.p.h1.h2.u1.li 2>行内标签(内联标签): 多个行内标签能同时显示在一行,宽度和高度取决于内容的尺寸 比如: ...

  9. oracle数据库导入、导出

    导出:exp BFXXLZ/BFXXLZ123@192.168.63.63:1521/ORCL file=d:\BFXXLZ.dmp 导入:imp BFXXLZ/BFXXLZ123@ORCL file ...

  10. 策划了个.NET控件的案例大赛

    任何一个产品的普及,都有一个过程: 1. 对新事物充满热情.喜欢尝鲜.或后急迫需要的人首先成为产品用户.他们总数很少,但是是产品用户的第一批种子. 2. 思想比较开放.能接受新事物的人会成为第二批用户 ...