Pyramid, Django, 和 Flask都是优秀的框架,为项目选择其中的哪一个都是伤脑筋的事。我们将会用三种框架实现相同功能的应用来更容易的对比三者。也可以直接跳到框架实战(Frameworks in Action)章节查看代码( code )。

世界上可选的基于Python的web框架有很多。Django, Flask, Pyramid, Tornado, Bottle, Diesel, Pecan, Falcon等等,都在争取开发者支持。作为一开发者从一堆选择中筛选出一个来完成项目将会成为下一个大工程。我们今天专注于Flask, Pyramid, 和 Django。它们涵盖了从小微项目到企业级的web服务。

为了更容易在三者中作出选择(至少更了解它们),我们将用每一个框架构建同样的应用并比较它们的代码,对于每一个方法我们会高亮显示它的优点和缺点。如果你只想要代码,直接跳到框架实战章节( Frameworks in Action), 或者查看其在 Github 上的代码。

Flask是一个面向简单需求小型应用的“微框架(microframework)”。Pyramid和Django都是面向大型应用的,但是有不同的拓展性和灵活性。Pyramid的目的是更灵活,能够让开发者为项目选择合适的工具。这意味着开发者能够选择数据库、URL结构、模板类型等等。Django目的是囊括web应用的所有内容,所以开发者只需要打开箱子开始工作,将Django的模块拉进箱子中。

Django包括一个开箱即用的 ORM ,而Pyramid和 Flask让开发者自己选择如何或者是否存储他们的数据。到目前为止对于非Django的web应用来说最流行的ORM是 SQLAlchemy ,同时还有多种其他选择,从 DynamoDB MongoDB 到简单本地存储的 LevelDB 或朴实的 SQLite 。Pyramid被设计为可使用任何数据持久层,甚至是还没有开发出来的。

2、关于框架

Django的"batteries included" 特性让开发者不需要提前为他们的应用程序基础设施做决定,因为他们知道Python已经深入到了web应用当中。Django已经内建了模板、表单、路由、认证、基本数据库管理等等。比较起来,Pyramid包括路由和认证,但是模板和数据库管理需要额外的库。

前面为 Flask和Pyramid apps选择组件的额外工作给那些使用案例不适用标准ORM的开发者提供了更多的灵活性,同样也给使用不同工作流和模版化系统的开发者们带来了灵活性。

Flask,作为三个框架里面最稚气的一个,开始于2010年年中。Pyramid框架是从 Pylons 项目开始的,在2010年底获得 Pyramid这个名字,虽然在2005年就已经发布了第一个版本。Django 2006年发布了第一个版本,就在Pylons项目(最后叫Pyramid)开始之后。Pyramid和Django都是非常成熟的框架,积累了众多插件和扩展以满足难以置信的巨大需求。

虽然Flask历史相对更短,但它能够学习之前出现的框架并且把注意力放在了微小项目上。它大多数情况被使用在一些只有一两个功能的小型项目上。例如 httpbin 一个简单的(但很强大的)调试和测试HTTP库的项目。

3. 社区

最具活力的社区当属Django,其有80,000个StackOverflow问题和一系列来自开发者和优秀用户的良好的博客。Flask和 Pyramid 社区并没有那么大,但它们的社区在邮件列表和IRC上相当活跃。StackOverflow上仅有5,000个相关的标签,Flask比Django小了15倍。在Github上,它们的star近乎相当,Django有11,300个,Flask有10,900个。

三个框架都使用的是BSD衍生的协议。 Flask Django 的协议是BSD 3条款,Pyramid的 Repoze Public License RPL 是BSD协议 4条款的衍生。

4. Bootstrapping

Django和Pyramid都内建bootstrapping工具。Flask没有包含类似的工具,因为Flask的目标用户不是那种 试图 构建大型 MVC 应用的人。

4.1 Flask

Flask的hello world应用非常的简单,仅仅单个Python文件的7行代码就够了。

# from http://flask.pocoo.org/ tutorial
from flask import Flask
app = Flask(__name__)
@app.route("/") # take note of this decorator syntax, it's a common pattern
def hello():
    return "Hello World!"
if __name__ == "__main__":
    app.run()

这是Flask没有bootstrapping工具的原因:没有它们的需求。从Flask主页上的Hello World特性看,没有构建Python web应用经验的开发者可以立即开始hacking。

对于各部分需要更多分离的项目,Flask有blueprints。例如,你可以将所有用户相关的函数放在users.py中,将销售相关的函数放在ecommerce.py中,然后在site.py中添加引用它们来结构化你的Flask应用。我们不会深入这个功能,因为它超出了我们展示demo应用的需求。

4.2 Pyramid

Pyramid 的 bootstrapping工具叫 pcreate,是Pyramid的组成部分. 之前的 Paste 工具套装提供了 bootstrapping ,但是从那之后被 Pyramid专用工具链替代了。

$ pcreate -s starter hello_pyramid # Just make a Pyramid project

Pyramid 比 Flask 适用于更大更复杂的应用程序. 因为这一点,它的 bootstrapping工具创建更大的项目骨架. Pyramid 同样加入了基本的配置文件,一个例子模版和用于将程序打包上传到 Python Package Index 的所有文件。

hello_pyramid
├── CHANGES.txt
├── development.ini
├── MANIFEST.in
├── production.ini
├── hello_pyramid
│   ├── __init__.py
│   ├── static
│   │   ├── pyramid-16x16.png
│   │   ├── pyramid.png
│   │   ├── theme.css
│   │   └── theme.min.css
│   ├── templates
│   │   └── mytemplate.pt
│   ├── tests.py
│   └── views.py
├── README.txt
└── setup.py

作为最后描述的框架,Pyramid的bootstrapper非常灵活. 不局限于一个默认的程序;pcreate 可以使用任意数量的项目模版. 包括我们上面用到的pcreate里面的"starter"的模版, 还有 SQLAlchemy- , ZODB -支持 scaffold项目 . 在 PyPi可以发现已经为 Google App Engine , jQuery Mobile , Jinja2 templating , modern frontend frameworks 做好的scaffolds, 还有更多~

4.3 Django

Django 也有自己的 bootstrap 工具, 内置在 django-admin 中.

django-admin startproject hello_django
django-admin startapp howdy # make an application within our project

Django 跟 Pyramid 区别在于: Django 由多个应用程序组成一个项目, 而 Pyramid 以及 Flask 项目是包含 View 和 Model 单一应用程序 . 理论上,  Flask 和 Pyramid 的项目允许存在多个 project/app, 不过在默认配置中只能有一个.

hello_django
├── hello_django
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── howdy
│   ├── admin.py
│   ├── __init__.py
│   ├── migrations
│   │   └── __init__.py
│   ├── models.py
│   ├── tests.py
│   └── views.py
└── manage.py

Django 默认只在项目中创建 空白的 model 和模板文件, 供新手参考的示范代码不多. 此外, 开发者在发布应用程序的时候, 还要自己配置, 这也是个麻烦.

bootstrap 工具的缺点是没有指导开发者如何打包应用.  对于那些没有经验的新手来说, 第一次部署应用将是个很头疼的问题. 像 django-oscar 这样的大社区, 项目都是打包好了, 放在 PyPi 上供大家安装. 但是 Github 上面的小项目缺少统一的打包方式.

一个Python应用能够响应HTTP请求将是一个伟大的开端,但是有可能你的大多数用户是没有兴趣使用curl与你的web应用交互的。幸运的是,这三个竞争者提供了使用自定义信息填充HTML的方法,以便让大伙们能够享受时髦的 Bootstrap 前端。

模板让你能够直接向页面注入动态信息,而不是采用AJAX。你只需要一次请求就可以获取整个页面以及所有的动态数据,这对用户体验来说是很好的。这对于手机网站来说尤其重要,因为一次请求花费的时间会更长。

所有的模板选项依赖于“上下文环境(context)”,其为模板转换为HTML提供了动态信息。模板的最简单的例子是填充已登录用户的名字以正确的迎接他们。也可以用AJAX获取这种动态信息,但是用一整个调用来填写用户的名字有点过头了,而同时模板又是这么的简单。

5.1 Django

我们使用的例子正如写的那么简单,假设我们有一个包含了用户名的funllname属性的user对象。在Python中我们这样向模板中传递当前用户:

def a_view(request):
    # get the logged in user
    # ... do more things
    return render_to_response(
        "view.html",
        {"user": cur_user}

拥有这个模板的上下文很简单,传入一个Python对象的字典和模板使用的数据结构。现在我们需要在页面上渲染他们的名字,以防页面忘了他们是谁。

<!-- view.html -->
<div class="top-bar row">
  <div class="col-md-10">
  <!-- more top bar things go here -->
  {% if user %}
  <div class="col-md-2 whoami">
    You are logged in as {{ user.fullname }}
  {% endif %}

首先,你会注意到这个  {% if user %} 概念。在Django模板中, {% 用来控制循环和条件的声明。这里的if user声明是为了防止那些不是用户的情况。匿名用户不应该在页面头部看到“你已经登录”的字样。

在if块内,你可以看到,包含名字非常的简单,只要用{{}}包含着我们要插入的属性就可以了。{{是用来向模板插入真实值的,如{{ user.fullname }}。

模板的另一个常用情况是展示一组物品,如一个电子商务网站的存货清单页面。

def browse_shop(request):
    # get items
    return render_to_response(
        "browse.html",
        {"inventory": all_items}

在模板中,我们使用同样的{%来循环清单中的所有条目,并填入它们各自的页面地址。

{% for widget in inventory %}
    <li><a href="/widget/{{ widget.slug }}/">{{ widget.displayname }}</a></li>
{% endfor %}

为了做大部分常见的模板任务,Django可以仅仅使用很少的结构来完成目标,因此很容易上手。