Flask知识点总结

Flask知识点总结

1. Flask Vs Django

- Flask
    - Flask确实很“轻”,不愧是Micro Framework,从Django转向Flask的开发者一定会如此感慨,除非二者均为深入使用过.
    - Flask自由、灵活,可扩展性强,第三方库的选择面广,开发时可以结合自己最喜欢用的轮子,也能结合最流行最强大的Python库.
    - 入门简单,即便没有多少web开发经验,也能很快做出网站
    - 非常适用于小型网站
    - 非常适用于开发web服务的API
    - 开发大型网站无压力,但代码架构需要自己设计,开发成本取决于开发者的能力和经验各方面性能均等于或优于Django.
    - Django自带的或第三方的好评如潮的功能,Flask上总会找到与之类似第三方库
    - Flask灵活开发,Python高手基本都会喜欢Flask,但对Django却可能褒贬不一
    - Flask与关系型数据库的配合使用不弱于Django,而其与NoSQL数据库的配合远远优于Django
    - Flask比Django更加Pythonic,与Python的philosophy更加吻合
- Django
    - Django太重了,除了web框架,自带ORM和模板引擎,灵活和自由度不够高.
    - Django能开发小应用,但总会有"杀鸡焉用牛刀"的感觉.
    - Django的自带ORM非常优秀,综合评价略高于SQLAlchemy,但SQLAlchemy的使用习惯和风格,更接近与SQL的操作
    - Django自带的模板引擎简单好用,但其强大程度和综合评价略低于Jinja
    - Django自带ORM也使Django与关系型数据库耦合度过高,如果想使用MongoDB等NoSQL数据,需要选取合适的第三方库,且总感觉Django+SQL才是天生一对的搭配,Django+NoSQL砍掉了Django的半壁江山
    - Django目前支持Jinja等非官方模板引擎
    - Django自带的数据库管理app好评如潮
    - Django非常适合企业级网站的开发:快速、靠谱、稳定
    - Django成熟、稳定、完善,但相比于Flask,Django的整体生态相对封闭
    - Django是Python web框架的先驱,用户多,第三方库最丰富,最好的Python库,如果不能直接用到Django中,也一定能找到与之对应的移植
    - Django上手也比较容易,开发文档详细、完善,相关资料丰富

参考博客

2. Flask中的请求上下文和应用上下文

2.1 请求上下文

请求上下文在flask 0.9版本之前,flask中只有“请求上下文”的概念。那什么是请求上下文呢?

我们先回忆一下在写flask程序的时候,经常会碰到直接调用像current_app、request、session、g等变量。这些变量看起来似乎是全局变量,但是实质上这些变量并不是真正意义上的全局变量。如果将这些变量设置为全局变量,试想一下,多个线程同时请求程序访问这些变量时势必会相互影响。但是如果不设置为全局变量,那在编写代码时每次都要显式地传递这些变量也是一件非常令人头疼的事情,而且还容易出错。

为了解决这些问题,flask设计时采取线程隔离的思路,也就是说在一次请求的一个线程中可以将其设置为全局变量,但是仅限于请求的这个线程内部,不同线程通过“线程标识符”来区别。这样就不会影响到其他线程的请求。flask实现线程隔离主要是使用werkzeug中的两个类:Local和LocalProxy,这里不再赘述,可以去看看werkzeug的源码了解一下实现过程。

实现线程隔离后,为了在一个线程中更加方便使用这些变量,flask中还有一种堆栈的数据结构(通过werkzeug的LocalStack实现),可以处理这些变量,但是并不直接处理这些变量。假如有一个程序得到一个请求,那么flask会将这个请求的所有相关信息进行打包,打包形成的东西就是处理请求的一个环境。flask将这种环境称为“请求上下文”(request context),之后flask会将这个请求上下文对象放到堆栈中。

这样,请求发生时,我们一般都会指向堆栈中的“请求上下文”对象,这样可以通过请求上下文获取相关对象并直接访问,例如current_app、request、session、g。还可以通过调用对象的方法或者属性获取其他信息,例如request.method。等请求结束后,请求上下文会被销毁,堆栈重新等待新的请求上下文对象被放入。

2.1 应用上下文

应用上下文的概念是在flask 0.9中增加的。
既然flask通过线程隔离的方式,将一些变量设置为线程内的“全局”可用。由于请求上下文中包含有当前应用相关的信息,那也就是说可以通过调用current_app就可以获取请求所在的正确应用而不会导致混淆。那为什么需要增加一个应用上下文的概念呢?

对于单应用单请求来说,使用“请求上下文”确实就可以了。然而,Flask的设计理念之一就是多应用的支持。当在一个应用的请求上下文环境中,需要嵌套处理另一个应用的相关操作时(这种情况更多的是用于测试或者在console中对多个应用进行相关处理),“请求上下文”显然就不能很好地解决问题了,因为魔法current_app无法确定当前处理的到底是哪个应用。如何让请求找到“正确”的应用呢?我们可能会想到,可以再增加一个请求上下文环境,并将其推入栈中。由于两个上下文环境的运行是独立的,不会相互干扰,所以通过调用栈顶对象的app属性或者调用current_app(current_app一直指向栈顶的对象)也可以获得当前上下文环境正在处理哪个应用。这种办法在一定程度上可行,但是如果说对第二个应用的处理不涉及到相关请求,那也就无从谈起“请求上下文”,更不可能建立请求上下文环境了。

为了应对这个问题,Flask中将应用相关的信息单独拿出来,形成一个“应用上下文”对象。这个对象可以和“请求上下文”一起使用,也可以单独拿出来使用。不过有一点需要注意的是:在创建“请求上下文”时一定要创建一个“应用上下文”对象。有了“应用上下文”对象,便可以很容易地确定当前处理哪个应用,这就是魔法current_app。在0.1版本中,current_app是对_request_ctx_stack.top.app的引用,而在0.9版本中current_app是对_app_ctx_stack.top.app的引用。其中_request_ctx_stack和_app_ctx_stack分别是存储请求上下文和应用上下文的栈。

这里举一个多应用的例子:

  • 假设有两个Flask应用:app1和app2。我们假定一种情形:在请求访问app1时,先要对app2进行一些操作,之后再处理app1内的请求。以下是一些分析过程:请求访问app1时,app1会生成一个请求上下文对象,并且使用with语句产生一个请求上下文环境。请求处理的所有过程都会在这个上下文环境中进行。当进入这个上下文环境时,Flask会将请求上下文对象推入_request_ctx_stack这个栈中,并且生成一个对应的应用上下文对象,将其推入_app_ctx_stack栈中。_app_ctx_stack栈顶指向的是app1;
  • 当在app1的请求上下文环境中需要对app2进行操作时,为了和app1的相关操作隔离开来,可以使用with语句建立一个app2的应用上下文环境。在这个过程中,会新建一个应用上下文对象,并将其推入_app_ctx_stack栈中。_app_ctx_stack栈顶指向的是app2;
  • 当退出app2的应用上下文环境,重新进入app1的请求上下文环境时,_app_ctx_stack栈会销毁app2的应用上下文对象,它的栈顶指向的是app1。

通过以上一个假象的例子,我们始终可以使用current_app来表示当前处理的Flask应用。

这里贴一段flask文档中对于请求上下文、应用上下文的说明。文档更加的容易理解。

请求上下文

If you look into how the Flask WSGI application internally works, you will
find a piece of code that looks very much like this::

    def wsgi_app(self, environ):
        with self.request_context(environ):
            try:
                response = self.full_dispatch_request()
            except Exception, e:
                response = self.make_response(self.handle_exception(e))
            return response(environ, start_response)

The method :meth:`~Flask.request_context` returns a new
:class:`~flask.ctx.RequestContext` object and uses it in combination with
the `with` statement to bind the context.  Everything that is called from
the same thread from this point onwards until the end of the `with`
statement will have access to the request globals (:data:`flask.request`
and others).

The request context internally works like a stack: The topmost level on
the stack is the current active request.
:meth:`~flask.ctx.RequestContext.push` adds the context to the stack on
the very top, :meth:`~flask.ctx.RequestContext.pop` removes it from the
stack again.  On popping the application's
:func:`~flask.Flask.teardown_request` functions are also executed.

Another thing of note is that the request context will automatically also
create an :ref:`application context ` when it's pushed and
there is no application context for that application so far.

应用上下文

The main reason for the application's context existance is that in the
past a bunch of functionality was attached to the request context in lack
of a better solution.  Since one of the pillar's of Flask's design is that
you can have more than one application in the same Python process.

So how does the code find the “right” application?  In the past we
recommended passing applications around explicitly, but that caused issues
with libraries that were not designed with that in mind for libraries for
which it was too inconvenient to make this work.

A common workaround for that problem was to use the
:data:`~flask.current_app` proxy later on, which was bound to the current
request's application reference.  Since however creating such a request
context is an unnecessarily expensive operation in case there is no
request around, the application context was introduced.

参考资料:
Flask中的请求上下文和应用上下文
应用上下文 — Flask 0.10.1 文档
请求上下文 — Flask 0.10.1 文档

你可能感兴趣的:(Flask知识点总结)