Flask、Django、Pyramid三个框架的对比
1.简介
在python web框架的世界里充满了选择。有Django,Flask,Pyramid,Tornado,Bottle,Diesel,Pecan,Falcon等等的来吸引开发者的注意。作为一个开发者,你想要从中选择一个框架来帮你完成项目,并且能继续做大事情。我们将关注Flask、Pyramid和Django。它们是微框架和商业级web服务的典范。
为了让你在这三个中做选择的时候能够更容易,我们会用每个框架来构建相同的应用,然后比较各自的代码,突出它们的优势和弱点。如果你想要代码,可以直接跳到 框架实战 章节或者在Github上下载代码。
Flask是一个微框架,主要面向需求简单的小应用。Pyramid和Django都是面向大的应用,但是在扩展性和灵活性上走了不同的路。Pyramid关注灵活性,让开发者选择合适的工具来开发项目。这意味着开发者可以选择数据库,URL结构,模板风格等等。Django的目标是提供web应用开发的一站式解决方案,所以相应的模块也就比较多。
Django包含了一个ORM模块,而Pyramid和Flask是让开发者来选择如何存储数据。针对非Django框架的最流行的ORM目前是SQLAlchemy,也有很多其他的选择,比如DynamoDB和MongoDB,亦或是像LevelDB和SQLite这样的简单本地持久化。Pyramid被设计可以使用任何持久层,甚至是还没做好的。
2 关于框架
Django的一站式解决的思路能让开发者不用在开发之前就在选择应用的基础设施上花费大量时间。Django有模板,表单,路由,认证,基本的数据库管理等等内建功能。与之相反,Pyramid包含路由和验证,但是模板和数据库管理需要第三方库。
用Flask和Pyramid来构建应用之前,选择组件的时候会给开发者带来更多的灵活性 ,可能有的应用场景不适合使用一个标准的ORM,或者需要与不同的工作流和模板系统交互。
Flask,这三个框架李最年轻的一个,创始与2010年年中。Pyramid框架来源于Pylons项目,在2010年末更名为Pyramid,它最早发布与2005年。Django发布于2006年,就在Pylons项目之后。Pyramid和Django是非常成熟的框架,积累了大量的插件和扩展来满足不同需要。
尽管Flask的历史较短,但它能够从以前的框架学到一些东西并且将它的目标设定在了小型项目上。它在一些仅有一两个功能的小型项目上得到了大量应用。比如httpbin这样的项目,简单但非常强大,是一个帮助debug和测试HTTP的库。
3 社区
Django的社区是最活跃的,在StackOverflow上有80000个相关问题和大量的博客和强大的用户。Flask和Pyramid的社区就没有这么大了,但是它们的社区在邮件列表和IRC里还是挺活跃的。在StackOverflow上只有5000个相关问题,Flask比Django的关注度小15倍。在Github上,它们的stars数相近,Django有11300个,Flask有10900个。
这三个框架都是处于BSD衍生的开源许可证书之下。Flask和Django的证书都是3条款BSD,而Pyramid的是RPL,是4条款BSD证书的衍生版。
4 入门引导
Django和Pyramid都有内建的引导工具。Flask没有,因为Flask的主要受众不是要构建大型MVC应用的。
4.1 Flask
Flask的Hello World应用的代码是最简单的,只用在一个Python文件里码7行代码就够了。
Python
1
2
3
4
5
6
7
8
9
10
|
# 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没有引导工具:因为它根本不需要。从上面的Hello World应用的特点来看,一个没什么Python web开发经验的人就可以很快的上手开始撸代码。
对于需要把组件分离开的项目,Flask有blueprints。例如,你可以这样构建你的应用,将与用户有关的功能放在user.py里,把与销售相关的功能放在ecommerce.py里,然后在site.py里引用并添加到你的应用里。我们暂时不会体验这个功能了,它超出了我们的实例应用的需要。
4.2 Pyramid
Pyramid的引导工具叫pcreate,它是Pyramid的一部分。之前有一个Paste的工具,不过后来被Pyramid指定的工具链替代了。
Python
1
|
$ pcreate –s starter hello_pyramid # Just make a Pyramid project
|
Pyramid希望能够做比Flask更大和复杂的应用。因此,它的引导工具会创建一个更大的项目框架。它里面包含一个配置文件,一个例子模板,还有文件能打包你的应用并上传到Python Package Index(PYPI)。
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
hello_pyramid
├── CHANGES.txt
├── development.ini
├── MANIFEST.in
├── production.ini
├── hello_pyramid
│ ├── __init__.py
│ ├── static
│ │ ├── pyramid–16×16.png
│ │ ├── pyramid.png
│ │ ├── theme.css
│ │ └── theme.min.css
│ ├── templates
│ │ └── mytemplate.pt
│ ├── tests.py
│ └── views.py
├── README.txt
└── setup.py
|
跟别的框架相比,Pyramid的引导工具特别的灵活。它没有被限制在一个默认应用里;pcreate可以使用任何数量的项目模板。包括我们在上面使用starter模板创建出来的,包含SQLAlchemy和ZODB支撑的项目。在PyPi上,可以找到依赖于Google App Engine,jQuery Mobile,Jinja2 templating,modern frontend frameworks等等的模板。
4.3 Django
Django也有自己的引导工具,它是django-admin的一部分。
Python
1
2
|
django–admin startproject hello_django
django–admin startapp howdy # make an application within our project
|
我们已经可以看到Django和Pyramid的一些区别。Django把一个项目分成各自独立的应用,而Pyramid和Flask认为一个项目应该是一个包含一些视图和模型的单个应用。也可以在Flask和Pyramid里复制出像Django那样的项目结构,但那不是默认的。
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
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只包含空的模型和模板文件,一个新的用户可以看一点例子代码就开始工作。它也能让开发者选择如何分配Django的应用。
这个引导工具的弱点是没有引导用户去打包他们的应用,新手往往会忽视这一点。如果一个开发者从未打包过应用,他们会发现在第一次部署的时候会有点乱糟糟的。一些有较大社区的项目,如django-oscar是被打包好的,并且能在PyPi上下载,但是在Github上,一些小的项目往往缺乏统一的打包。
5 模板
有一个能回复HTTP请求的Python应用是个很好的开始,但是你的用户不会用curl来与你的应用交互。幸运的是,这三个框架都提供了简单的方法能让你向HTML里填充自定义信息,可以让大家领略到你酷炫的前端技术。
模板能让你直接向页面中嵌入动态信息,而不用发送AJAX请求。这对于用户体验是有利的,你能一次加载整个页面和它的动态数据。这对于移动端网站是非常重要的,能够节省数秒钟时间。
所有的我们使用的模板都依赖上下文提供动态信息,并将其通过模板渲染进HTML。模板的最简单使用场景是很流行的一个例子,给已登录的用户的名字打招呼。也可以使用AJAX来获取动态信息,但需要一个调用来获取用户名信息,这样可能有点费事了,毕竟用模板来实现这么简单。
5.1 Django
我们这里的使用场景非常简单,假定我们有一个user对象,它有一个fullname的属性,里面包含一个用户的名字。在Python里,我们会这样把当前用户信息传递给模板:
Python
1
2
3
4
5
6
7
|
def a_view(request):
# get the logged in user
# … do more things
return render_to_response(
“view.html”,
{“user”: cur_user}
)
|
构成模板上下文非常简单,只需要将一个有Python对象和数据结构的字典传进模板即可。现在,我们需要通过名称将其渲染进页面,以免我们忘了它们是什么。
Python
1
2
3
4
5
6
7
8
9
10
11
|
<!— view.html —>
<div class=“top-bar row”>
<div class=“col-md-10”>
<!— more top bar things go here —>
</div>
{% if user %}
<div class=“col-md-2 whoami”>
You are logged in as {{ user.fullname }}
</div>
{% endif %}
</div>
|
首先,我们看到{% if user %}这样的结构。在Django模板里,{% 是用来一些控制语句的比如循环和条件式。if use 的声明是用来针对如果没有user的情况。匿名用户不应该在页面上方看到“you are logged in as”的字样。
在if块里,你可以看到包括名字是被简单的封装在了{{}}里。{{是用来放实际中嵌入模板的值,比如{{ user.fullname }}。
模板的另一个常见用法是展示一组东西,比如一个商业网站的存货页面。
Python
1
2
3
4
5
6
|
def browse_shop(request):
# get items
return render_to_response(
“browse.html”,
{“inventory”: all_items}
)
|
模板里,我们可以使用相同的 {% 来循环出存货数据里的所有项目,并且将其填充到URL和各自页面。
Python
1
2
3
|
{% for widget in inventory %}
<li><a href=“/widget/{{ widget.slug }}/”>{{ widget.displayname }}</a></li>
{% endfor %}
|
对于大多数普通的模板任务来说,Django能够轻松实现,非常容易上手。
5.2 Flask
Flask默认使用一个受Django启发而发展起来的名为Jinja2的模板,但也可以通过配置来使用其他的语言。一个码农可能会将Django和Jinja模板弄混。事实上,所有上面的Django模板的例子在Jinja2里也是好使的。我们就不重复上面的例子了,我们来看看Jinja2比Django模板的一些更有表现力的特点。
Jinja和Django模板都提供一个叫过滤的特性,一个列表可以在被展示前传给一个函数。一个博客如果包含分类的特性,就可以使用过滤来将一个分类下的文章筛选出来。
Python
1
2
3
4
5
|
<!— Django —>
<div class=“categories”>Categories: {{ post.categories|join:“, “ }}</div>
<!— now in Jinja —>
<div class=“categories”>Categories: {{ post.categories|join(“, “) }}</div>
|
在Jinja的模板语言里,可以把任何数量的参数传给过滤器,因为Jinja像调用一个Python函数的方式来看待它,用圆括号来封装参数。Django使用冒号来分隔过滤器名和参数,这样就只能传递一个参数了。
Jinja和Django的 for 循环很相似。我们来看看它们的区别。在Jinja2, for-else-endfor 结构让你能对一个列表进行迭代,也能处理列表为空的情况。
Python
1
2
3
4
5
6
7
8
|
{% for item in inventory %}
<div class=“display-item”>{{ item.render() }}</div>
{% else %}
<div class=“display-warn”>
<h3>No items found</h3>
<p>Try another search, maybe?</p>
</div>
{% endfor %}
|
在Django版本的功能是一样的,只是使用了 for-empty-endfor 这样的结构替换了 for-else-endfor 的结构。
Python
1
2
3
4
5
6
7
8
|
{% for item in inventory %}
<div class=“display-item”>{{ item.render }}</div>
{% empty %}
<div class=“display-warn”>
<h3>No items found</h3>
<p>Try another search, maybe?</p>
</div>
{% endfor %}
|
除了上面的语法差别,Jinja2提供更多的执行环境的控制和高级特性。例如,它可以关闭潜在的危险特性来运行不受信任的模板,或者提前编译模板以确保它们有效。
5.3 Pyramid
跟Flask类似,Pyramid支持很多模板语言(包括Jinja2和Mako),但它有一个默认的模板。Pyramid使用Chameleon,一个ZPT(the Zope Page Template)语言的实现。我们来看一个例子,把用户的名字添加到网站顶端。相应的Python代码有点类似但更加明确,不用调用render_template函数。
Python
1
2
3
4
|
@view_config(renderer=‘templates/home.pt’)
def my_view(request):
# do stuff…
return {‘user’: user}
|
但是我们的模板看起来就差别挺大了。ZPT是一个基于XML的模板标准,所以我们使用XSLT类似的声明来管理数据。
1
2
3
4
5
6
7
8
9
|
<div class=“top-bar row”>
<div class=“col-md-10”>
<!— more top bar things go here —>
</div>
<div tal:condition=“user”
tal:content=“string:You are logged in as ${user.fullname}”
class=“col-md-2 whoami”>
</div>
</div>
|
Chameleon 有三种不同的模板动作命名空间。TAL(template attribute language)提供基本的条件语句,基本的字符串格式化,根据标记过滤内容。上面的例子只使用了TAL来完成相应工作。对于更加高级的工作,就需要TALES和METAL了。TALES(Template Attribute Language Expression Syntax)提供一些高级的字符串格式化,评估Python表达式,和引入表达式和模板。
METAL(Macro Expansion Template Attribute Language)是Chameleon模板里最强大(和最复杂)的部分。Macros是可扩展的,可以被定义得像槽一样,在macro被调用的时候填充上。
6. 框架实战
对每个框架,我们来看看做一个叫wut4lunch的应用,这是一个社交网络,可以告诉整个英特网这你晚饭吃了什么。就用这个想法启动,是个游戏规则改变者。这个应用会是一个简单的接口,能让用户PO出他们午饭吃了什么,还可以看到别人吃了什么。首页看起来会是这个样子:
6.1 用Flask做的应用
这个最短的实现,仅需34行Python代码和一个22行的Jinja模板。首先,我们有一些例行公事的任务,比如初始化我们的应用和引入ORM。
Python
1
2
3
4
5
6
7
8
9
10
11
|
from flask import Flask
# For this example we’ll use SQLAlchemy, a popular ORM that supports a
# variety of backends including SQLite, MySQL, and PostgreSQL
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
# We’ll just use SQLite here so we don’t need an external database
app.config[‘SQLALCHEMY_DATABASE_URI’] = ‘sqlite:///test.db’
db = SQLAlchemy(app)
|
首先,我们来看我们的模型,跟其他两个例子里的会差不多。
Python
1
2
3
4
5
|
class Lunch(db.Model):
“””A single lunch”””
id = db.Column(db.Integer, primary_key=True)
submitter = db.Column(db.String(63))
food = db.Column(db.String(255))
|
Wow,就是这么简单。最难的地方是找到合适的SQLAlchemy 数据类型并为数据库的里的字段选择一个合适的长度。使用我们的模型是相当简单的,要感谢SQLAlchemy 的查询语法,我们在接下来会看到。
建立我们的提交表单灰常简单。引入Flask-WTForms 并且弄好字段类型,你可以看到表单看起来就像我们的模型。主要的区别是新的提交按钮和关于食品的提示和提交者名字的字段。
SECRET_KEY 字段在应用的配置里是被WTForms使用的,用来创建CSRF token。它也被 isdangerous 使用,用来创建cookie和其他数据。
Python
1
2
3
4
5
6
7
8
9
10
|
from flask.ext.wtf import Form
from wtforms.fields import StringField, SubmitField
app.config[‘SECRET_KEY’] = ‘please, tell nobody’
class LunchForm(Form):
submitter = StringField(u‘Hi, my name is’)
food = StringField(u‘and I ate’)
# submit button will read “share my lunch!”
submit = SubmitField(u‘share my lunch!’)
|
让表单在浏览器里显示出来意味着模板里需要有它。我们会在下面做这件事。
Python
1
2
3
4
5
6
7
|
from flask import render_template
@app.route(“/”)
def root():
lunches = Lunch.query.all()
form = LunchForm()
return render_template(‘index.html’, form=form, lunches=lunches)
|
好的,发生了什么?我们有了用Lunch.query.all() 查询出来的一些已经PO出来的午餐,实例化一个表单,让用户PO出他们自己的美食冒险。为了简洁,变量用相同名称传递给模板,但这不是必须的。
Python
1
2
3
4
5
6
|
<html>
<title>Wut 4 Lunch</title>
<b>What are people eating?</b>
<p>Wut4Lunch is the latest social network where you can tell all your friends
about your noontime repast!</p>
|
这是真正的模板了,我们循环出所有展示和吃掉的午餐,并将它们展示在
- 标签里。这与之前看到的例子差不多。
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<ul>
{% for lunch in lunches %}
<li><strong>{{ lunch.submitter|safe }}</strong> just ate <strong>{{ lunch.food|safe }}</strong>
{% else %}
<li><em>Nobody has eaten lunch, you must all be starving!</em></li>
{% endfor %}
</ul>
<b>What are YOU eating?</b>
<form method=“POST” action=“/new”>
{{ form.hidden_tag() }}
{{ form.submitter.label }} {{ form.submitter(size=40) }}
<br/>
{{ form.food.label }} {{ form.food(size=50) }}
<br/>
{{ form.submit }}
</form>
</html>
|
模板里
转载自演道,想查看更及时的互联网产品技术热点文章请点击http://go2live.cn