title: session和蓝图
date: 2018-10-29 17:18:00
tags:flask
categories: 十月,2018
session
- 这是一个基本的代码
from flask import Flask
#1. 实例化Flask对象
app = Flask(__name__)
# 2. 设置路由
"""
app.url_map = [
('/index',index),
('/login',login),
]
"""
@app.route('/index')
def index():
return "index"
if __name__ == '__main__':
# 3. 启动socket服务端
app.run()
# 4. 用户请求到来
app.__call__
app.wsgi_app
app.request_class
app.session_interface
- 用户请求到来,先执行__call__方法,然后执行wsgi_app函数
- def wsgi_app(self, environ, start_response):
- """
- 1.获取environ并对其进行再次封装
- 2.从environ中获取名称为session的cookie,解密,反序列化
- 3.两个东西放到Local中
- """
# ctx = RequestContext(self, environ) #self是app对象,environ请求相关的原始数据
# ctx.request = Request(environ)
# ctx.session = None
ctx = self.request_context(environ)
error = None
try:
try:
# 将ctx放到"Local"上
# 执行 SecureCookieSessionInterface.open_session,去rookie中获取值并给ctx.session重新赋值
ctx.push()
# 4.执行视图函数
# 5."Local"获取session,加密,序列化 ==》写cookie
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.handle_exception(e)
except:
error = sys.exc_info()[1]
raise
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None
# 6."Local"位置清空
ctx.auto_pop(error)
执行视图函数
def full_dispatch_request(self):
self.try_trigger_before_first_request_functions()
try:
request_started.send(self)
rv = self.preprocess_request()
if rv is None:
rv = self.dispatch_request() # 调用视图函数
except Exception as e:
rv = self.handle_user_exception(e)
#视图函数执行完毕后,进行善后工作
return self.finalize_request(rv)
open_session
def open_session(self, app, request):
s = self.get_signing_serializer(app)
if s is None:
return None
val = request.cookies.get(app.session_cookie_name)
if not val:
return self.session_class()
max_age = total_seconds(app.permanent_session_lifetime)
try:
data = s.loads(val, max_age=max_age)
return self.session_class(data)
except BadSignature:
return self.session_class()
save_session
def save_session(self, app, session, response):
domain = self.get_cookie_domain(app)
path = self.get_cookie_path(app)
# If the session is modified to be empty, remove the cookie.
# If the session is empty, return without setting the cookie.
if not session:
if session.modified:
response.delete_cookie(
app.session_cookie_name,
domain=domain,
path=path
)
return
# Add a "Vary: Cookie" header if the session was accessed at all.
if session.accessed:
response.vary.add('Cookie')
if not self.should_set_cookie(app, session):
return
httponly = self.get_cookie_httponly(app)
secure = self.get_cookie_secure(app)
samesite = self.get_cookie_samesite(app)
expires = self.get_expiration_time(app, session)
val = self.get_signing_serializer(app).dumps(dict(session))
response.set_cookie(
app.session_cookie_name,
val,
expires=expires,
httponly=httponly,
domain=domain,
path=path,
secure=secure,
samesite=samesite
)
蓝图
- 目标:给开发者提供目录结构
- 其他:
- 自定义模板、静态文件
- 功能式架构
- 在功能式架构中,按照每部分代码的功能来组织你的应用。
- 所有模板放到同一个文件夹中,静态文件放在另一个文件夹中,
- 而视图放在第三个文件夹中。
- yourapp/
__init__.py
static/
templates/
home/
control_panel/
admin/
views/
__init__.py
home.py
control_panel.py
admin.py
models.py
- 除了yourapp/views/__init__.py,在yourapp/views/文件夹中
- 的每一个.py文件都是一个蓝图。在yourapp/__init__.py中,
- 我们将加载这些蓝图并在我们的Flask()对象中注册它们。
- 分区式架构
- 在分区式架构中,按照每一部分所属的蓝图来组织你的应用。
- 管理面板的所有的模板,视图和静态文件放在一个文件夹中,
- 用户控制面板的则放在另一个文件夹中。
- yourapp/
__init__.py
admin/
__init__.py
views.py
static/
templates/
home/
__init__.py
views.py
static/
templates/
control_panel/
__init__.py
views.py
static/
templates/
models.py
- 在像上面列举的分区式结构,每一个yourapp/之下的文件夹都是
- 一个独立的蓝图。所有的蓝图通过顶级的__init__.py注册到Flask()中。
- 某一类url添加前缀
- 给一类url添加before_request
基本用法
- 让我们看看来自Facebook例子的一个蓝图的代码:
- facebook/views/profile.py
- from flask import Blueprint, render_template
profile = Blueprint('profile', __name__)
@profile.route('/<user_url_slug>')
def timeline(user_url_slug):
# 做些处理
return render_template('profile/timeline.html')
@profile.route('/<user_url_slug>/photos')
def photos(user_url_slug):
# 做些处理
return render_template('profile/photos.html')
@profile.route('/<user_url_slug>/about')
def about(user_url_slug):
# 做些处理
return render_template('profile/about.html')
- 要想创建一个蓝图对象,你需要importBlueprint()类并用参数name
- import_name初始化。通常用__name__,一个表示当前模块的特殊的
- Python变量,作为import_name的取值。
- 假如使用分区式架构,你得告诉Flask某个蓝图是有着自己的模板和静态文件夹的。
- 下面是这种情况下我们的定义大概的样子:
- profile = Blueprint('profile', __name__,
template_folder='templates',
static_folder='static')
- 现在我们已经定义好了蓝图。是时候向Flask app注册它了
- facebook/__init__.py
- from flask import Flask
from .views.profile import profile
app = Flask(__name__)
app.register_blueprint(profile)
-现在在fackbook/views/profile.py中定义的路径(比如/<user_url_slug>)
会被注册到应用中,就像是被通过@app.route()定义的。
使用一个动态的URL前缀
- 继续看Facebook的例子,注意到所有的个人信息路由都以<user_url_slug>开头
并把它传递给视图函数。我们想要用户通过类似http://facebook.com/john.doe的
URL访问个人信息。通过给所有的蓝图的路由定义一个动态前缀,我们可以结束这种单调
的重复。
- 蓝图允许我们定义静态的或动态的前缀。举个例子,我们可以告诉Flask蓝图中
- 所有的路由应该以/profile作为前缀;这样是一个静态前缀。在Fackbook这个例子中,
- 前缀取决于用户浏览的是谁的个人信息。他们在URL对应片段中输入的文本将决定我们输
- 出的视图;这样是一个动态前缀。
- 我们可以选择何时定义我们的前缀。我们可以在下列两个时机中选择一个定义前缀:
当我们实例化Blueprint()类的时候,或当我们在app.register_blueprint()中注册的时候。
下面我们在实例化的时候设置URL前缀:
facebook/views/profile.py
from flask import Blueprint, render_template
profile = Blueprint('profile', __name__, url_prefix='/<user_url_slug>')
# [...]
下面我们在注册的时候设置URL前缀:
facebook/__init__.py
from flask import Flask
from .views.profile import profile
app = Flask(__name__)
app.register_blueprint(profile, url_prefix='/<user_url_slug>')
- 尽管这两种方式在技术上没有区别,最好还是在注册的同时定义前缀。这使得前缀的定义
可以集中到顶级目录中。因此,我推荐使用url_prefix。
使用一个动态子域名
- 今天,许多SaaS应用提供用户一个子域名来访问他们的软件。举个例子,Harvest,
是一个针对顾问的日程管理软件,它在yourname.harvestapp.com给你提供了一个控制
面板。下面我将展示在Flask中如何像这样自动生成一个子域名。
- 在这一节,我将使用一个允许用户创建自己的网站的应用作为例子。假设我们的应用有
三个蓝图分别针对以下的部分:用户注册的主页面,可用于建立自己的网站的用户管理面板,
用户的网站。考虑到这三个部分相对独立,我们将用分区式结构组织起来。
sitemaker/
__init__.py
home/
__init__.py
views.py
templates/
home/
static/
home/
dash/
__init__.py
views.py
templates/
dash/
static/
dash/
site/
__init__.py
views.py
templates/
site/
static/
site/
models.py
url | 蓝图目录 | 作用 |
---|---|---|
sitemaker.com/ | sitemaker/home | 一个普通的蓝图。包括用于index.html,about.html和pricing.html的视图,模板和静态文件。 |
bigdaddy.sitemaker.com | sitemaker/site | 这个蓝图使用了动态子域名,并包括了用户网站的一些元素。等下我们来看看用于实现这个蓝图的一些代码。 |
bigdaddy.sitemaker.com/admin | sitemaker/dash | 这个蓝图将使用一个动态子域名和一个URL前缀,把这一节的技术和上一节的结合起来。 |
定义动态子域名的方式和定义URL前缀一样。同样的,我们可以选择在蓝图文件夹中,
或在顶级目录的__init__.py中定义它。这一次,我们还是在sitemaker/__init__.py
中放置所有的定义。
sitemaker/__init__.py
from flask import Flask
from .site import site
app = Flask(__name__)
app.register_blueprint(site, subdomain='<site_subdomain>')
既然我们用的是分区式架构,蓝图将在sitemaker/site/__init__.py定义。
sitemaker/site/__init__py
from flask import Blueprint
from ..models import Site
# 注意首字母大写的Site和全小写的site是两个完全不同的变量。
# Site是一个模块,而site是一个蓝图。
site = Blueprint('site', __name__)
@site.url_value_preprocessor
def get_site(endpoint, values):
query = Site.query.filter_by(subdomain=values.pop('site_subdomain'))
g.site = query.first_or_404()
# 在定义site后才import views。视图模块需要import 'site',所以我们需要确保在import views之前定义site。
from . import views
现在我们已经从数据库中获取可以向请求子域名的用户展示的站点信息了。
为了使Flask能够支持子域名,你需要修改配置变量SERVER_NAME。
config.py
SERVER_NAME = 'sitemaker.com'
threading.local【和flask无任何关系】
作用:为每个线程创建一个独立的空间,使得线程对自己的空间中的数据进行操作(数据隔离)。
import threading
from threading import local
import time
obj = local()
def task(i):
obj.xxxxx = i
time.sleep(2)
print(obj.xxxxx,i)
for i in range(10):
t = threading.Thread(target=task,args=(i,))
t.start()
问题:
- 如何获取一个线程的唯一标记? threading.get_ident()
- 根据字典自定义一个类似于threading.local功能?
import time
import threading
DIC = {}
def task(i):
ident = threading.get_ident()
if ident in DIC:
DIC[ident]['xxxxx'] = i
else:
DIC[ident] = {'xxxxx':i }
time.sleep(2)
print(DIC[ident]['xxxxx'],i)
for i in range(10):
t = threading.Thread(target=task,args=(i,))
t.start()
- 根据字典自定义一个为每个协程开辟空间进行存取数据。
import time
import threading
import greenlet
DIC = {}
def task(i):
# ident = threading.get_ident()
ident = greenlet.getcurrent()
if ident in DIC:
DIC[ident]['xxxxx'] = i
else:
DIC[ident] = {'xxxxx':i }
time.sleep(2)
print(DIC[ident]['xxxxx'],i)
for i in range(10):
t = threading.Thread(target=task,args=(i,))
t.start()
- 通过getattr/setattr 构造出来 threading.local的加强版(协程)
import time
import threading
try:
import greenlet
get_ident = greenlet.getcurrent
except Exception as e:
get_ident = threading.get_ident
class Local(object):
DIC = {}
def __getattr__(self, item):
ident = get_ident()
if ident in self.DIC:
return self.DIC[ident].get(item)
return None
def __setattr__(self, key, value):
ident = get_ident()
if ident in self.DIC:
self.DIC[ident][key] = value
else:
self.DIC[ident] = {key:value}
obj = Local()
def task(i):
obj.xxxxx = i
time.sleep(2)
print(obj.xxxxx,i)
for i in range(10):
t = threading.Thread(target=task,args=(i,))
t.start()