Flask 源代码阅读笔记
我认为我已经养成了一个坏习惯。在使用一个框架过程中对它的内部原理非常感兴趣,有时候须要花不少精力才
明确,这也导致了学习的缓慢,但换来的是对框架的内部机理的熟悉,正如侯捷所说,源代码面前,了无秘密。这也是
本文产生的直接原因。
一.flask session原理
二. flask扩展import 原理
当导入 from falsk.ext.example import E是将会运行flask/ext/__init__.py
def setup():from ..exthook import ExtensionImporterimporter = ExtensionImporter(['flask_%s', 'flaskext.%s'], __name__)importer.install()
install将会向sys.meta_path加入模块装载类,当import时会调用其find_module,假设返回非None,会调用load_module载入
比方当我们 from flask.ext.script import Manager时
会调用find_module('flask.ext.script')。prefinx是flask.ext所以将会调用load_module()
此时将会尝试import flask_script模块或flaskext.script
def install(self):sys.meta_path[:] = [x for x in sys.meta_path if self != x] + [self]def find_module(self, fullname, path=None):if fullname.startswith(self.prefix):return selfdef load_module(self, fullname):modname = fullname.split('.', self.prefix_cutoff)[self.prefix_cutoff]for path in self.module_choices:realname = path % modname__import__(realname)
三. flask sqlalchemy原理
@teardowndef shutdown_session(response_or_exc):if app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN']:if response_or_exc is None:self.session.commit()self.session.remove()return response_or_exc
response_or_exc为异常值。默认为sys.exc_info()[1]
上面self.session.remove()表示每次请求后都会销毁self.session,为什么要这么做呢?
这就要说说sqlalchemy的session对象了。
from sqlalchemy.orm import sessionmaker
session = sessionmaker()
一帮我们会通过sessionmaker()这个工厂函数创建session,但这个session并不能用在多线程中,为了支持多线程
所以在多线程中一帮都是例如以下使用session
from sqlalchemy.orm import scoped_session, sessionmaker
session = scoped_session(sessionmaker())
class scoped_session(object):def __init__(self, session_factory, scopefunc=None):self.session_factory = session_factoryif scopefunc:self.registry = ScopedRegistry(session_factory, scopefunc)else:self.registry = ThreadLocalRegistry(session_factory)
__init__中,session_factory是创建session的工厂函数,而sessionmaker就是一工厂函数(事实上是定义了__call__的
class ThreadLocalRegistry(ScopedRegistry):def __init__(self, createfunc):self.createfunc = createfuncself.registry = threading.local()def __call__(self):try:return self.registry.valueexcept AttributeError:val = self.registry.v
从上面__call__能够看出。每次都会创建新的session,并发在线程本地变量中。你可能会好奇__call__是在哪里调用的?
def instrument(name):def do(self, *args, **kwargs):return getattr(self.registry(), name)(*args, **kwargs)return dofor meth in Session.public_methods:setattr(scoped_session, meth, instrument(meth))
正如我们所示,当我们调用session.query将会调用 getattr(self.registry(), 'query'),self.registry()就是
# Which stack should we use?
_app_ctx_stack is new in 0.9 connection_stack = _app_ctx_stack or _request_ctx_stack def __init__(self, app=None, use_native_unicode=True, session_options=None): session_options.setdefault( 'scopefunc', connection_stack.__ident_func__ ) self.session = self.create_scoped_session(session_options) def create_scoped_session(self, options=None): """Helper factory method that creates a scoped session.""" if options is None: options = {} scopefunc=options.pop('scopefunc', None) return orm.scoped_session( partial(_SignallingSession, self, **options), scopefunc=scopefunc )
我们看到scopefunc被设置为connection_stack.__ident_func__。而connection_stack就是flask中app上下文。
假设你看过前一篇文章你就知道__ident_func__事实上就是在多线程中就是thrading.get_ident。也就是线程id
我们看看ScopedRegistry是怎样通过_操作的
class ScopedRegistry(object):def __init__(self, createfunc, scopefunc):self.createfunc = createfuncself.scopefunc = scopefuncself.registry = {}def __call__(self):key = self.scopefunc()try:return self.registry[key]except KeyError:return self.registry.setdefault(key, self.createfunc())
代码也非常easy,事实上也就是依据线程id创建相应的session对象,到这里我们基本已经了解了flask_sqlalchemy的
1.flask_sqlalchemy是否能使用ThreadLocalRegistry?
大部分情况都是能够的。但假设wsgi对多并发使用的是greenlet的模式就不适用了
2.上面create_scoped_session中partial是干嘛的?
前面我们说过scoped_session的session_factory是可调用对象,但_SignallingSession类并未定义__call__,所以通过partial支持
到这里你就知道为什么每次请求结束要self.session.remove(),不然为导致存放session的字段太大
class Role(db.Model):__tablename__ = 'roles'id = db.Column(db.Integer, primary_key=True)name = db.Column(db.String(64), unique=True)users = db.relationship('User', backref='role', lazy='dynamic')class User(db.Model):__tablename__ = 'users'id = db.Column(db.Integer, primary_key=True)username = db.Column(db.String(64), unique=True, index=True)role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
假设role是已经获取的一个Role的实例
lazy:dynamic => role.users不会返回User的列表, 返回的是sqlalchemy.orm.dynamic.AppenderBaseQuery对象
当运行role.users.all()是才会真正运行sql,这种优点就是能够继续过滤
lazy:select => role.users直接返回User实例的列表,也就是直接运行sql
注意:db.session.commit仅仅有在对象有变化时才会真的运行update
四. flask moment原理
假设使用了flask bootstrap。仅仅须要在最后加入下面代码就可以(须要jquery支持)
{% block scripts %}
{{ super() }}
{{ moment.include_moment() }}
{{ moment.lang('zh-cn') }}
{% endblock %}
flask moment还提供了过了多长时间统计,refresh为True时。每分钟刷新一次,refresh也可为详细的刷新时间,
{{ moment(current_time).fromNow(refresh=True) }}
def init_app(self, app):if not hasattr(app, 'extensions'):app.extensions = {}app.extensions['moment'] = _momentapp.context_processor(self.context_processor)@staticmethoddef context_processor():return {'moment': current_app.extensions['moment']}
通过app.context_processor给模板上下文加入了额为属性
def render_template(template_name_or_list, **context):
ctx.app.update_template_context(context)
而flask bootstrap是通过app.jinja_env.globals['bootstrap_find_resource'] = bootstrap_find_resource实现的
rv.globals.update(url_for=url_for,get_flashed_messages=get_flashed_messages,config=self.config,# request, session and g are normally added with the# context processor for efficiency reasons but for imported# templates we also want the proxies in there.request=request,session=session,g=g)
但我在看源代码时发现_default_template_ctx_processor也会注入g。request,例如以下
def _default_template_ctx_processor():"""Default template context processor. Injects `request`,`session` and `g`."""reqctx = _request_ctx_stack.topappctx = _app_ctx_stack.toprv = {}if appctx is not None:rv['g'] = appctx.gif reqctx is not None:rv['request'] = reqctx.requestrv['session'] = reqctx.sessionreturn rv
这不是反复嘛,有啥必要呢?
哈哈,认真看上面rv.globals.update的凝视部分能大概明确。
flask模板能够使用宏,须要使用import导入,此时导入的模板不能訪问不能訪问当前模板的本地变量,仅仅能使用全局变量。
这也就是为什么global中有g,request,session的理由,也即是为了支持在宏中使用g对象
转载于:https://www.cnblogs.com/brucemengbm/p/6713057.html
Flask 源代码阅读笔记相关推荐
- OBS源代码阅读笔记
链客,专为开发者而生,有问必答! 此文章来自区块链技术社区,未经允许拒绝转载. obs配置文件加载:bool OBSBasic::InitBasicConfig(); OBS认证信息加载,貌似还没有实 ...
- Vector和LinkedList源代码阅读笔记
Vector 1 三个成员变量 elementData: Container elementCount: 有效长度 capacityIncrement: 每次增加的长度 2, 其构造函数没什么特别的, ...
- Lua 5.1.1 源代码阅读笔记
http://blog.csdn.net/hamenny/article/details/4506130 转载于:https://www.cnblogs.com/tinytiny/p/3560841. ...
- linux源代码阅读笔记 find_entry分析
78 static struct buffer_head * find_entry(struct m_inode * dir,79 const char * name, int namelen, st ...
- sunplus 8202v iop源代码阅读笔记——3
原创文章,如有转载,请注明出处:http://blog.csdn.net/desert2009sz/article/details/6878106 3. 中断服务程序 中断服务程序,需要 ...
- sunplus 8202v iop源代码阅读笔记——2
原创文章,如有转载,请注明出处:http://blog.csdn.net/desert2009sz/article/details/6877873 三.iop主程序 iop.asm是iop的主程序,在 ...
- sunplus 8202v iop源代码阅读笔记——1
原创文章,如有转载,请注明出处:http://blog.csdn.net/desert2009sz/article/details/6877589 一.前言 对于汇编,我一直很敬畏,特别是多过2000 ...
- 《深入实践Spring Boot》阅读笔记之三:核心技术源代码分析
为什么80%的码农都做不了架构师?>>> 刚关注的朋友,可以回顾前两篇文章: 基础应用开发 分布式应用开发 上篇文章总结了<深入实践Spring Boot>的第二部 ...
- Mina源码阅读笔记(一)-整体解读
2019独角兽企业重金招聘Python工程师标准>>> 今天的这一节,将从整体上对mina的源代码进行把握,网上已经有好多关于mina源码的阅读笔记,但好多都是列举了一下每个接口或者 ...
最新文章
- 多线程编程之死锁已经死锁产生的原因
- springcloud api-gateway详解
- Valentine's Day Round hdu 5176 The Experience of Love [好题 带权并查集 unsigned long long]
- seo html空格影响,这一对HTML标签嵌套对SEO优化的影响,居然99%的人不知道!
- Codeforces 1196D RGB Substring
- priorityqueue 的 add和offer方法有区别吗_日常在家安吉白茶应该如何去保存?城市与农村存放的方法有区别吗...
- 十大经典数据挖掘算法之k-means
- mysql常用cmd指令_Mysql cmd 常用命令
- jmeter xml 请求_Jmeter学习笔记(十六)——HTTP请求之content-type
- 最长公共子序列-dp
- 我一定要找到它FreeEIM
- 吴恩达机器学习 9.机器学习系统设计
- powershell 模拟IE行为
- php工具箱mysql启动不_解决php工具箱(phpStudy)Apache启动成功,MySql无法启动的问题...
- 硅谷天才CEO被罢免10天后绝地反击,重夺控制权
- Git学习笔记(二)——Git的分支管理、储藏和标签
- linux shrink dev sd,Virt相关命令(转)
- jenkins 403 No valid crumb was included in the request 解决方案
- 【计算机视觉与深度学习 北京邮电大学 鲁鹏 视频笔记】1. 线性分类器
- 【读书笔记】《心流:最优体验心理学》——序
热门文章
- linux ssh和sftp区别,Linux SSH和SFTP配置
- python更新_MacOS升级python版本
- linux 高级文件IO
- go https client
- libjpeg学习4:libjpeg-turbo之YUV
- 解压tar.xz文件
- mysql群集配置_MySQL主主集群配置
- @Transactional +自定义注解不生效_SpringBoot之路(三)SpringDataJpa中常用注解的使用...
- SpringBoot : SpringBoot自定义的ApplicationContext实现类
- 【分布式】分布式架构-ESB SOA