Você está na página 1de 18

9/19/2015

DjangovsFlaskvsPyramid:ChoosingaPythonWebFramework

(/)

Login (/login)

Find an expert (/find-an-expert)

Help others (/be-an-expert)

Posts (/posts)

More

(/visit/lob-150812)

Django vs Flask vs Pyramid: Choosing a Python


Web Framework
Ryan Brown
Ryan works on Openstack Heat at Red Hat and has written Python for web,
orchestration, and backend applications large and small.

FREE WORKSHOP: Django vs Flask vs PyramidRyan Brown is giving a free virtual workshop to help guide you on picking the right Python
framework and answer your questions. >> Sign up to secure a spot
(http://docs.google.com/forms/d/1h1gunjoc3FqcPGRWsQFZFvhy9iPluh3paK4tf9H7Edc/viewform?embedded=true)
TL;DR: Pyramid, Django, and Flask are all excellent frameworks, and choosing just one for a
project is hard. We'll see working apps with identical functionality in all three frameworks to
make comparing the three easier. Skip to Frameworks in Action for the code
(https://github.com/ryansb/wut4lunch_demos).

1 Introduction
The world of Python web frameworks is full of choices. Django, Flask, Pyramid,
Tornado, Bottle, Diesel, Pecan, Falcon, and many more are competing for developer
mindshare. As a developer you want to cut the legions of options down to the one
that will help you finish your project and get on to the Next Big Thing (tm). We'll focus
on Flask, Pyramid, and Django. Their ideal cases span from micro-project to
enterprise-size web service.

COMMUNIT

https://www.airpair.com/python/posts/djangoflaskpyramid

1/18

9/19/2015

DjangovsFlaskvsPyramid:ChoosingaPythonWebFramework

To help make the choice between the three easier (or at least more informed), we'll

NEED 1 ON

build the same application in each framework and compare the code, highlighting the
strengths and weaknesses of each approach. If you just want the code, skip straight
to Frameworks in Action or view the code on Github
(https://github.com/ryansb/wut4lunch_demos).
Flask is a "microframework" primarily aimed at small applications with simpler

TABLE OF C

1 Introdu

2 About t

requirements. Pyramid and Django are both aimed at larger applications, but take

3 Commu

different approaches to extensibility and flexibility. Pyramid targets flexibility and lets

4 Bootstr

the developer use the right tools for their project. This means the developer can
choose the database, URL structure, templating style, and more. Django aims to
include all the batteries a web application will need so developers need only open the

5 Templa

box and start working, pulling in Django's many modules as they go.
Django includes an ORM (http://en.wikipedia.org/wiki/Object-relational_mapping) out

6 Framew

of the box, while Pyramid and Flask leave it to the developer to choose how (or if)
they want their data stored. The most popular ORM for non-Django web applications
is SQLAlchemy (http://www.sqlalchemy.org/) by far, but there are plenty of other

7 Summa

options from DynamoDB (http://aws.amazon.com/dynamodb/) and MongoDB


(http://www.mongodb.org/) to simple local persistence like LevelDB
(https://github.com/google/leveldb) or plain SQLite (http://www.sqlite.org/). Pyramid
is designed to use any persistence layer, even yet-to-be-invented ones.

2 About the Frameworks


Django's "batteries included" approach makes it easy for developers who know
Python already to dive in to web applications quickly without needing to make a lot of
decisions about their application's infrastructure ahead of time. Django has for
templating, forms, routing, authentication, basic database administration, and more
built in. In contrast, Pyramid includes routing and authentication, but templating and
database administration require external libraries.
The extra work up front to choose components for Flask and Pyramid apps yields
more flexibility for developers whose use case doesn't fit a standard ORM, or who
need to interoperate with different workflows or templating systems.
Flask, the youngest of the three frameworks, started in mid-2010. The Pyramid
framework began life in the Pylons project
(http://www.pylonsproject.org/about/history) and got the name Pyramid in late 2010,
though the first release was in 2005. Django had its first release in 2006, shortly after
the Pylons (eventually Pyramid) project began. Pyramid and Django are extremely
mature frameworks, and have accumulated plugins and extensions to meet an
incredibly large range of needs.
Though Flask has a shorter history, it has been able to learn from frameworks that
have come before and has set its sights firmly on small projects. It is clearly used
most often in smaller projects with just one or two functions. One such project is
httpbin (http://httpbin.org/), a simple (but extremely powerful) helper for debugging
and testing HTTP libraries.

3 Community
https://www.airpair.com/python/posts/djangoflaskpyramid

2/18

3 Community

9/19/2015

DjangovsFlaskvsPyramid:ChoosingaPythonWebFramework

The prize for most active community goes to Django with 80,000 StackOverflow
questions and a healthy set of blogs from developers and power users. The Flask and
Pyramid communities aren't as large, but their communities are quite active on their
mailing lists and on IRC. With only 5,000 StackOverflow questions tagged, Flask is 15x
smaller than Django. On Github, they have a nearly identical number of stars with
11,300 for Django, and 10,900 for Flask.
All three frameworks are available under BSD-derived permissive licenses. Both
Flask's (https://raw.githubusercontent.com/mitsuhiko/flask/master/LICENSE) and
Django's (https://raw.githubusercontent.com/django/django/master/LICENSE)
licenses are 3-clause BSD, while Pyramid's Repoze Public License RPL
(http://repoze.org/license.html) is a derivative of the 4-clause BSD license.

4 Bootstrapping
Django and Pyramid both come with bootstrapping tools built in. Flask includes
nothing of the sort because Flask's target audience isn't trying to build large MVC
(http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller)
applications.

4.1 Flask
Flask's HelloWorld app has to be the simplest out there, clocking in at a puny 7 lines
of code in a single Python file.

#fromhttp://flask.pocoo.org/tutorial

2
3

fromflaskimportFlask
app=Flask(__name__)

python

4
5

@app.route("/")#takenoteofthisdecoratorsyntax,it'sacommonpattern

defhello():

7
8
9

return"HelloWorld!"

10

if__name__=="__main__":
app.run()

Like learning from posts like this? Subscribe for more!

This is why there aren't bootstrapping tools for Flask: there isn't a demand for them.
From the above Hello World featured on Flask's homepage, a developer with no
experience building Python web applications can get hacking immediately.
For projects that need more separation between components, Flask has blueprints
(http://flask.pocoo.org/docs/0.10/blueprints/). For example, you could structure your
Flask app with all user-related functions in users.py and your sales-related functions
in ecommerce.py , then import them and add them to your app in site.py . We won't
go over this functionality, as it's beyond the needs of our demo app.

4.2 Pyramid
Pyramid's bootstrapping tool is called pcreate which is part of Pyramid. Previously
the Paste (http://pythonpaste.org/) suite of tools provided bootstrapping for but has
since been replaced with a Pyramid-specific toolchain.
https://www.airpair.com/python/posts/djangoflaskpyramid

3/18

9/19/2015
1

DjangovsFlaskvsPyramid:ChoosingaPythonWebFramework

$pcreatesstarterhello_pyramid#JustmakeaPyramidproject

bash

Like learning from posts like this? Subscribe for more!

Pyramid is intended for bigger and more complex applications than Flask. Because of
this, its bootstrapping tool creates a bigger skeleton project. It also throws in basic
configuration files, an example template, and the files to package your application for
uploading to the Python Package Index (https://pypi.python.org/).

hello_pyramid

2
3

CHANGES.txt
development.ini

4
5
6

MANIFEST.in
production.ini
hello_pyramid

7
8

__init__.py
static

9
10

pyramid16x16.png
pyramid.png

11
12
13

theme.css
theme.min.css
templates

14
15

mytemplate.pt
tests.py

16
17

views.py
README.txt

18

setup.py

markup

Like learning from posts like this? Subscribe for more!

As in the rest of the framework, Pyramid's bootstrapper is incredibly flexible. It's not
limited to one default application; pcreate can use any number of project templates.
Included in pcreate there is the "starter" template we used above, along with
SQLAlchemy- and ZODB (http://www.zodb.org/en/latest/)-backed scaffold projects.
On PyPi it's possible to find ready-made scaffolds for Google App Engine
(https://pypi.python.org/pypi/pyramid_appengine/), jQuery Mobile
(https://github.com/Pylons/pyramid_jqm), Jinja2 templating
(https://pypi.python.org/pypi/jinja2-alchemy-starter), modern frontend frameworks
(https://pypi.python.org/pypi/pyramid_modern), and many more.

4.3 Django
Django also has its own bootstrapping tool built in as a part of djangoadmin .

1
2

djangoadminstartprojecthello_django
djangoadminstartapphowdy#makeanapplicationwithinourproject

python

Like learning from posts like this? Subscribe for more!

We can already see one of the ways Django differs from Pyramid. Django separates a
project into individual applications, where Pyramid and Flask expect a project to be a
single "application" with several views or models. It's possible to replicate the
project/app distinction in Flask and Pyramid, but the notion does not exist by default.

https://www.airpair.com/python/posts/djangoflaskpyramid

4/18

9/19/2015
1
2

hello_django
hello_django

3
4

__init__.py
settings.py

5
6

urls.py
wsgi.py

7
8
9

howdy
admin.py
__init__.py

10
11

migrations
__init__.py

12
13

models.py
tests.py

14
15

views.py
manage.py

DjangovsFlaskvsPyramid:ChoosingaPythonWebFramework
markup

Like learning from posts like this? Subscribe for more!

By default Django only includes empty model and template files, so a new user sees a
bit less example code to start out. It also (unfortunately) leaves the choice of how to
distribute their application to the developer.
The downside of the bootstrap tool not guiding users to package their apps is that
novice users won't. If a developer hasn't packaged an app before, they'll find
themselves rudely surprised upon their first deploy. Projects with a large community
like django-oscar (https://github.com/tangentlabs/django-oscar) are packaged and
available on PyPi, but smaller projects on Github often to lack uniform packaging.

5 Templating
Just having a Python application that can respond to HTTP requests is a great start,
but it's a good bet that most of your users won't be interested in using curl to
interact with your web app. Fortunately, all three contenders provide an easy way to
fill in HTML with custom info, and let folks enjoy your swanky Bootstrap
(http://getbootstrap.com/) frontend.
Templating lets you inject dynamic information directly into your page without using
making AJAX requests. This is nice from a user experience perspective since you only
need to make one round-trip to get the full page and all its dynamic data. This is
especially important on mobile sites where round trips can take multiple seconds.
All the templating options we'll see rely on a "context" that provides the dynamic
information for the template to render into HTML. The simplest use case for a
template would be to populate a logged-in user's name to greet them properly. It
would be possible to use AJAX to get this sort of dynamic information, but requiring a
whole call just to fill in a user's name would be a bit excessive when templates are
this easy.

5.1 Django
Our example use case is about as easy as it gets, assuming that we have a user
object that has a fullname property containing a user's name. In Python we'd pass
the current user to the template like so:

https://www.airpair.com/python/posts/djangoflaskpyramid

5/18

9/19/2015
1
2

defa_view(request):
#gettheloggedinuser

3
4

#...domorethings
returnrender_to_response(

5
6

"view.html",
{"user":cur_user}

DjangovsFlaskvsPyramid:ChoosingaPythonWebFramework
python

Like learning from posts like this? Subscribe for more!

Populating the template context is as simple as passing a dictionary of the Python


objects and data structures the template should use. Now we need to render their
name to the page, just in case they forget who they are.
python

<!view.html>

2
3
4
5
6

<divclass="topbarrow">
<divclass="colmd10">
<!moretopbarthingsgohere>
</div>
{%ifuser%}

7
8
9
10

<divclass="colmd2whoami">
Youareloggedinas{{user.fullname}}
</div>
{%endif%}

11

</div>

Like learning from posts like this? Subscribe for more!

First, you'll notice the {%ifuser%} construct. In Django templates {% is used for
control statements like loops and conditionals. The ifuser statement is there to
guard against cases where there is not a user. Anonymous users shouldn't see "you
are logged in as" in the site header.
Inside the if block, you can see that including the name is as simple as wrapping the
property we want to insert in {{}} . The {{ is used to insert actual values into the
template, such as {{user.fullname}} .
Another common use for templates is displaying groups of things, like the inventory
page for an ecommerce site.

1
2

defbrowse_shop(request):
#getitems

3
4
5
6

returnrender_to_response(
"browse.html",
{"inventory":all_items}
)

python

Like learning from posts like this? Subscribe for more!

In the template we can use the same {% to loop over all the items in the inventory,
and to fill in the URL to their individual page.

1
2
3

{%forwidgetininventory%}
<li><ahref="/widget/{{widget.slug}}/">{{widget.displayname}}</a></li>
{%endfor%}

python

Like learning from posts like this? Subscribe for more!

To do most common templating tasks, Django can accomplish the goal with just a few
constructs, making it easy to get started.

https://www.airpair.com/python/posts/djangoflaskpyramid

6/18

9/19/2015

DjangovsFlaskvsPyramid:ChoosingaPythonWebFramework

5.2 Flask
Flask uses the Django-inspired Jinja2 (http://jinja.pocoo.org/) templating language by
default but can be configured to use another language. A programmer in a hurry
couldn't be blamed for mixing up Django and Jinja templates. In fact, both the Django
examples above work in Jinja2. Instead of going over the same examples, let's look at
the places that Jinja2 is more expressive than Django templating.
Both Jinja and Django templates provide a feature called filtering, where a list can be
passed through a function before being displayed. A blog that features post
categories might make use of filters to display a post's categories in a commaseparated list.

1
2
3

<!Django>
<divclass="categories">Categories:{{post.categories|join:","}}</div>

4
5

<!nowinJinja>
<divclass="categories">Categories:{{post.categories|join(",")}}</div>

python

Like learning from posts like this? Subscribe for more!

In Jinja's templating language it's possible to pass any number of arguments to a filter
because Jinja treats it like a call to a Python function, with parenthesis surrounding
the arguments. Django uses a colon as a separator between the filter name and the
filter argument, which limits the number of arguments to just one.
Jinja and Django for loops are also similar. Let's see where they differ. In Jinja2, the
for-else-endfor construct lets you iterate over a list, but also handle the case where
there are no items.

1
2
3

{%foritemininventory%}
<divclass="displayitem">{{item.render()}}</div>
{%else%}

4
5
6
7
8

<divclass="displaywarn">
<h3>Noitemsfound</h3>
<p>Tryanothersearch,maybe?</p>
</div>
{%endfor%}

python

Like learning from posts like this? Subscribe for more!

The Django version of this functionality is identical, but uses for-empty-endfor instead
of for-else-endfor.

1
2
3
4
5

{%foritemininventory%}
<divclass="displayitem">{{item.render}}</div>
{%empty%}
<divclass="displaywarn">
<h3>Noitemsfound</h3>

6
7
8

<p>Tryanothersearch,maybe?</p>
</div>
{%endfor%}

python

Like learning from posts like this? Subscribe for more!

Other than the syntactic differences above, Jinja2 provides more control over its
execution environment and advanced features. For example, it's possible to disable
potentially dangerous features to safely execute untrusted templates, or to compile
templates ahead of time to ensure their validity.
https://www.airpair.com/python/posts/djangoflaskpyramid

7/18

9/19/2015

DjangovsFlaskvsPyramid:ChoosingaPythonWebFramework

5.3 Pyramid
Like Flask, Pyramid supports many templating languages (including Jinja2 and Mako)
but ships with one by default. Pyramid uses Chameleon
(http://docs.pylonsproject.org/projects/pyramid-chameleon/en/latest/), an
implementation of ZPT (http://wiki.zope.org/ZPT/FrontPage) (the Zope Page Template)
templating language. Let's look back at our first example, adding a user's name to the
top bar of our site. The Python code looks much the same except that we don't need
to explicitly call a render_template function.

1
2
3
4

@view_config(renderer='templates/home.pt')
defmy_view(request):
#dostuff...
return{'user':user}

python

Like learning from posts like this? Subscribe for more!

But our template looks pretty different. ZPT is an XML-based templating standard, so
we use XSLT-like statements to manipulate data.
python

1
2

<divclass="topbarrow">
<divclass="colmd10">

3
4
5
6

<!moretopbarthingsgohere>
</div>
<divtal:condition="user"
tal:content="string:Youareloggedinas${user.fullname}"

7
8
9

class="colmd2whoami">
</div>
</div>

Like learning from posts like this? Subscribe for more!

Chameleon actually has three different namespaces for template actions. TAL
(template attribute language) provides basics like conditionals, basic string
formatting, and filling in tag contents. The above example only made use of TAL to
complete its work. For more advanced tasks, TALES and METAL are required. TALES
(Template Attribute Language Expression Syntax) provides expressions like advanced
string formatting, evaluation of Python expressions, and importing expressions and
templates.
METAL (Macro Expansion Template Attribute Language) is the most powerful (and
complex) part of Chameleon templating. Macros are extensible, and can be defined
as having slots that are filled when the macro is invoked.

6 Frameworks in Action
For each framework let's take a look at making an app called wut4lunch, a social
network to tell the whole internet what you ate for lunch. Free startup idea right
there, totally a gamechanger. The application will be a simple interface that allows
users to post what they had for lunch and to see a list of what other users ate. The
home page will look like this when we're done.

https://www.airpair.com/python/posts/djangoflaskpyramid

8/18

9/19/2015

DjangovsFlaskvsPyramid:ChoosingaPythonWebFramework

6.1 Demo App with Flask


The shortest implementation clocks in at 34 lines of Python and a single 22 line Jinja
template. First we have some housekeeping tasks to do, like initializing our app and
pulling in our ORM.
python

1
2
3

fromflaskimportFlask

4
5
6
7

#varietyofbackendsincludingSQLite,MySQL,andPostgreSQL
fromflask.ext.sqlalchemyimportSQLAlchemy

8
9
10
11

#Forthisexamplewe'lluseSQLAlchemy,apopularORMthatsupportsa

app=Flask(__name__)
#We'lljustuseSQLiteheresowedon'tneedanexternaldatabase
app.config['SQLALCHEMY_DATABASE_URI']='sqlite:///test.db'
db=SQLAlchemy(app)

Like learning from posts like this? Subscribe for more!

Now let's take a look at our model, which will remain almost the same for our other
two examples as well.
python

1
2

classLunch(db.Model):
"""Asinglelunch"""

3
4
5

id=db.Column(db.Integer,primary_key=True)
submitter=db.Column(db.String(63))
food=db.Column(db.String(255))

Like learning from posts like this? Subscribe for more!

Wow, that's pretty easy. The hardest part was finding the right SQLAlchemy data
types (http://docs.sqlalchemy.org/en/rel_0_9/core/types.html) and picking a length for
our String fields in the database. Using our models is also extremely simple, thanks
to the SQLAlchemy query syntax
(http://docs.sqlalchemy.org/en/latest/orm/query.html) we'll see later.
Building our submission form is just as easy. After importing Flask-WTForms
(https://flask-wtf.readthedocs.org/en/latest/) and the correct field types, you can see
the form looks quite a bit like our model. The main difference is the new submit
button and prompts for the food and submitter name fields.
The SECRET_KEY field in the app config is used by WTForms to create CSRF
(http://en.wikipedia.org/wiki/Cross-site_request_forgery) tokens. It is also used by
itsdangerous (http://pythonhosted.org/itsdangerous/) (included in Flask) to sign
https://www.airpair.com/python/posts/djangoflaskpyramid

9/18

9/19/2015

DjangovsFlaskvsPyramid:ChoosingaPythonWebFramework

cookies and other data.

1
2
3
4
5
6
7
8
9
10

fromflask.ext.wtfimportForm
fromwtforms.fieldsimportStringField,SubmitField

python

app.config['SECRET_KEY']='please,tellnobody'
classLunchForm(Form):
submitter=StringField(u'Hi,mynameis')
food=StringField(u'andIate')
#submitbuttonwillread"sharemylunch!"
submit=SubmitField(u'sharemylunch!')

Like learning from posts like this? Subscribe for more!

Making the form show up in the browser means the template has to have it. We'll
pass that in below.
python

1
2
3
4

fromflaskimportrender_template

5
6
7

lunches=Lunch.query.all()
form=LunchForm()
returnrender_template('index.html',form=form,lunches=lunches)

@app.route("/")
defroot():

Like learning from posts like this? Subscribe for more!

Alright, what just happened? We got a list of all the lunches that have already been
posted with Lunch.query.all() , and instantiated a form to let the user post their own
gastronomic adventure. For simplicity, the variables are passed into the template with
the same name, but this isn't required.
markup

1
2
3
4

<html>
<title>Wut4Lunch</title>
<b>Whatarepeopleeating?</b>

5
6

<p>Wut4Lunchisthelatestsocialnetworkwhereyoucantellallyourfriends
aboutyournoontimerepast!</p>

Like learning from posts like this? Subscribe for more!

Here's the real meat of the template, where we loop through all the lunches that have
been eaten and display them in a <ul> . This almost identical to the looping example
we saw earlier.

https://www.airpair.com/python/posts/djangoflaskpyramid

10/18

9/19/2015
1
2
3
4
5
6
7
8
9
10

DjangovsFlaskvsPyramid:ChoosingaPythonWebFramework

python
<ul>
{%forlunchinlunches%}
<li><strong>{{lunch.submitter|safe}}</strong>justate<strong>{{lunch.food|safe}}</
strong>
{%else%}

<li><em>Nobodyhaseatenlunch,youmustallbestarving!</em></li>
{%endfor%}
</ul>
<b>WhatareYOUeating?</b>

11
12
13
14

<formmethod="POST"action="/new">
{{form.hidden_tag()}}
{{form.submitter.label}}{{form.submitter(size=40)}}

15
16
17
18
19

<br/>
{{form.food.label}}{{form.food(size=50)}}
<br/>
{{form.submit}}
</form>

Like learning from posts like this? Subscribe for more!

The <form> section of the template just renders the form labels and inputs from the
WTForm object we passed into the template in the root() view. When the form is
submitted, it'll send a POST request to the /new endpoint which will be processed by
the function below.

1
2
3

fromflaskimporturl_for,redirect

4
5

defnewlunch():
form=LunchForm()

6
7

ifform.validate_on_submit():
lunch=Lunch()

form.populate_obj(lunch)

python

@app.route(u'/new',methods=[u'POST'])

9
10

db.session.add(lunch)
db.session.commit()

11

returnredirect(url_for('root'))

Like learning from posts like this? Subscribe for more!

After validating the form data, we put the contents into one of our Model objects and
commit it to the database. Once we've stored the lunch in the database it'll show up
in the list of lunches people have eaten.

1
2

if__name__=="__main__":
db.create_all()#makeoursqlalchemytables

app.run()

python

Like learning from posts like this? Subscribe for more!

Finally, we have to do a (very) little bit of work to actually run our app. Using
SQLAlchemy we create the table we use to store lunches, then start running the route
handlers we wrote.

6.2 Demo App with Django


The Django version of wut4lunch is similar to the Flask version, but is spread across
several files in the Django project. First, let's look at the most similar portion: the
database model. The only difference between this and the SQLAlchemy version is the
slightly different syntax for declaring a database field that holds text.

https://www.airpair.com/python/posts/djangoflaskpyramid

11/18

9/19/2015
1
2

DjangovsFlaskvsPyramid:ChoosingaPythonWebFramework
python

#fromwut4lunch/models.py
fromdjango.dbimportmodels

3
4
5

classLunch(models.Model):
submitter=models.CharField(max_length=63)

food=models.CharField(max_length=255)

Like learning from posts like this? Subscribe for more!

On to the form system. Unlike Flask, Django has a built-in form system that we can
use. It looks much like the WTForms module we used in Flask with different syntax.
python

1
2

fromdjangoimportforms
fromdjango.httpimportHttpResponse

fromdjango.shortcutsimportrender,redirect

4
5

from.modelsimportLunch

6
7

#Createyourviewshere.

8
9
10

classLunchForm(forms.Form):
"""Formobject.LooksalotliketheWTFormsFlaskexample"""

11

submitter=forms.CharField(label='Yourname')

12

food=forms.CharField(label='Whatdidyoueat?')

Like learning from posts like this? Subscribe for more!

Now we just need to make an instance of LunchForm to pass in to our template.

lunch_form=LunchForm(auto_id=False)

2
3

defindex(request):

lunches=Lunch.objects.all()

5
6

returnrender(
request,

7
8

'wut4lunch/index.html',
{

'lunches':lunches,

10
11

'form':lunch_form,
}

12

python

Like learning from posts like this? Subscribe for more!

The render function is a Django shortcut that takes the request, the template path,
and a context dict . Similar to Flask's render_template , but it also takes the incoming
request.
python

defnewlunch(request):

2
3

l=Lunch()
l.submitter=request.POST['submitter']

l.food=request.POST['food']

5
6

l.save()
returnredirect('home')

Like learning from posts like this? Subscribe for more!

Saving the form response to the database is different, instead of using a global
database session Django lets us call the model's .save() method and handles
session management transparently. Neat!

https://www.airpair.com/python/posts/djangoflaskpyramid

12/18

9/19/2015

DjangovsFlaskvsPyramid:ChoosingaPythonWebFramework

Django provides some nice features for us to manage the lunches that users have
submitted, so we can delete lunches that aren't appropriate for our site. Flask and
Pyramid don't provide this automatically, and not having to write Yet Another Admin
Page when making a Django app is certainly a feature. Developer time isn't free! All
we had to do to tell Django-admin about our models is add two lines to
wut4lunch/admin.py .

fromwut4lunch.modelsimportLunch

admin.site.register(Lunch)

python

Like learning from posts like this? Subscribe for more!

Bam. And now we can add and delete entries without doing any extra work.
Lastly, let's take a look at the differences in the homepage template.
python

<ul>

2
3

{%forlunchinlunches%}
<li><strong>{{lunch.submitter}}</strong>justate<strong>{{lunch.food}}</strong></li>

4
5

{%empty%}
<em>Nobodyhaseatenlunch,youmustallbestarving!</em>

{%endfor%}

</ul>

Like learning from posts like this? Subscribe for more!

Django has a handy shortcut for referencing other views in your pages. The url tag
makes it possible for you to restructure the URLs your application serves without
breaking your views. This works because the url tag looks up the URL of the view
mentioned on the fly.

1
2

<formaction="{%url'newlunch'%}"method="post">
{%csrf_token%}

{{form.as_ul}}

4
5

<inputtype="submit"value="Iatethis!"/>
</form>

python

Like learning from posts like this? Subscribe for more!

The form is rendered with different syntax, and we need to include a CSRF token
manually in the form body, but these differences are mostly cosmetic.

6.3 Demo App with Pyramid


Finally, let's take a look at the same program in Pyramid. The biggest difference from
Django and Flask here is the templating. Changing the Jinja2 template very slightly
was enough to solve our problem in Django. Not so this time, Pyramid's Chameleon
template syntax is more reminiscent of XSLT (http://en.wikipedia.org/wiki/XSLT) than
anything else.

https://www.airpair.com/python/posts/djangoflaskpyramid

13/18

9/19/2015

DjangovsFlaskvsPyramid:ChoosingaPythonWebFramework

1
2

<!pyramid_wut4lunch/templates/index.pt>
<divtal:condition="lunches">

<ul>

4
5

<divtal:repeat="lunchlunches"tal:omittag="">
<lital:content="string:${lunch.submitter}justate${lunch.food}"/>

6
7

</div>
</ul>

</div>

9
10

<divtal:condition="not:lunches">
<em>Nobodyhaseatenlunch,youmustallbestarving!</em>

11

</div>

python

Like learning from posts like this? Subscribe for more!

Like in Django templates, a lack of the for-else-endfor construct makes the logic
slightly more verbose. In this case, we end up with if-for and if-not-for blocks to
provide the same functionality. Templates that use XHTML tags may seem foreign
after using Django- and AngularJS-style templates that use {{ or {% for control
structures and conditionals.
One of the big upsides to the Chameleon templating style is that your editor of choice
will highlight the syntax correctly, since the templates are valid XHTML. For Django
and Flask templates your editor needs to have support for those templating
languages to highlight correctly.

python

<b>WhatareYOUeating?</b>

2
3
4

<formmethod="POST"action="/newlunch">
Name:${form.text("submitter",size=40)}

5
6

<br/>
Whatdidyoueat?${form.text("food",size=40)}

<br/>

8
9

<inputtype="submit"value="Iatethis!"/>
</form>

10

</html>

Like learning from posts like this? Subscribe for more!

The form rendering is slightly more verbose in Pyramid because the


pyramid_simpleform doesn't have an equivalent to Django forms' form.as_ul

function, which renders all the form fields automatically.


Now let's see what backs the application. First, we'll define the form we need and
render our homepage.
python

#pyramid_wut4lunch/views.py

classLunchSchema(Schema):

3
4

submitter=validators.UnicodeString()
food=validators.UnicodeString()

5
6

@view_config(route_name='home',

renderer='templates/index.pt')

8
9

defhome(request):
lunches=DBSession.query(Lunch).all()

10
11

form=Form(request,schema=LunchSchema())
return{'lunches':lunches,'form':FormRenderer(form)}

Like learning from posts like this? Subscribe for more!

The query syntax to retrieve all the lunches is familiar from Flask because both demo
applications use the popular SQLAlchemy ORM (http://www.sqlalchemy.org/) to
provide persistent storage. In Pyramid lets you return your template's context
https://www.airpair.com/python/posts/djangoflaskpyramid

14/18

9/19/2015

DjangovsFlaskvsPyramid:ChoosingaPythonWebFramework

dictionary directly instead of needing to call a special render function. The


@view_config decorator automatically passes the returned context to the template to

be rendered. Being able to skip calling the render method makes functions written
for Pyramid views easier to test, since the data they return isn't obscured in a
template renderer object.
python

@view_config(route_name='newlunch',

renderer='templates/index.pt',

3
4

request_method='POST')
defnewlunch(request):

5
6

l=Lunch(
submitter=request.POST.get('submitter','nobody'),

food=request.POST.get('food','nothing'),

8
9

10
11

withtransaction.manager:
DBSession.add(l)

12
13

raiseexc.HTTPSeeOther('/')

Like learning from posts like this? Subscribe for more!

Form data is easy to retrieve from Pyramid's request object, which automatically
parsed the form POST data into a dict that we can access. To prevent multiple
concurrent requests from all accessing the database at the same time, the
ZopeTransactions module provides context managers
(https://docs.python.org/2/reference/datamodel.html#context-managers) for
grouping database writes into logical transactions and prevent threads of your
application from stomping on each others' changes, which can be a problem if your
views share a global session and your app receives a lot of traffic.

7 Summary
Pyramid is the most flexible of the three. It can be used for small apps as we've seen
here, but it also powers big-name sites like Dropbox. Open Source communities like
Fedora (https://fedoraproject.org/) choose it for applications like their community
badges system (https://badges.fedoraproject.org/), which receives information about
events from many of the project's tools to award achievement-style badges to users.
One of the most common complaints about Pyramid is that it presents so many
options it can be intimidating to start a new project.
By far the most popular framework is Django, and the list of sites that use it is
impressive. Bitbucket, Pinterest, Instagram, and The Onion use Django for all or part
of their sites. For sites that have common requirements, Django chooses very sane
defaults and because of this it has become a popular choice for mid- to large-sized
web applications.
Flask is great for developers working on small projects that need a fast way to make a
simple, Python-powered web site. It powers loads of small one-off tools, or simple
web interfaces built over existing APIs. Backend projects that need a simple web
interface that is fast to develop and will require little configuration often benefit from
Flask on the frontend, like jitviewer (https://bitbucket.org/pypy/jitviewer) which
provides a web interface for inspecting PyPy just-in-time compiler logs.

https://www.airpair.com/python/posts/djangoflaskpyramid

15/18

9/19/2015

DjangovsFlaskvsPyramid:ChoosingaPythonWebFramework

All three frameworks came up with a solution to our small list of requirements, and
we've been able to see where they differ. Those differences aren't just cosmetic, and
they will change how you design your product and how fast you ship new features
and fixes. Since our example was small, we've seen where Flask shines and how
Django can feel clunky on a small scale. Pyramid's flexibility didn't become a factor
because our requirements stayed the same, but in the real world new requirements
are thrown in constantly.

7.1 Credits
Logos in the title image are from the Flask (http://flask.pocoo.org/community/logos/)
Django (https://www.djangoproject.com/community/logos/) and Pyramid
(http://www.pylonsproject.org/projects/pyramid/about) project web sites.
This article owes many thanks to its reviewers, Remy DeCausemaker, Ross Delinger,
and Liam Middlebrook, for tolerating many early drafts.
In its current form this article incorporates comments and corrections from Adam
Chainz, bendwarn, Sergei Maertens, Tom Leo, and wichert. (alphabetical order)

How would you rate the quality of this post?


3 stars (average)

LOGIN TO SAVE 3 STAR REVIEW (/POSTS/CONTRIBUTORS/542337D15A8A960B0024BC1B)

Reviews (5)

Rating 4.4/5

Adrian Garrigos 6 months ago

Because of the 3 REAL side by side analysis in the code...


Upvote - Reply

Rob Kellington 6 months ago

Did exactly what was expected and did it clearly with example code.
Upvote - Reply

Nodiel Clavijo 3 months ago

Very useful.
Upvote - Reply

Brian Kim 2 months ago

great explanation with example code. however, the code wasnt out of the box
which gave a noobie like me some difficultly in getting the latter two frameworks up
an running...
Upvote - Reply

Rithum Labs 2 days ago

Well written and concise!


Upvote - Reply

https://www.airpair.com/python/posts/djangoflaskpyramid

16/18

9/19/2015

DjangovsFlaskvsPyramid:ChoosingaPythonWebFramework

Like this post? Get Python content like this by email


Enter your email address

SEND TO "YOUR NAME <>"

Tagged under

Python (/posts/tag/python)

Django (/posts/tag/django)

Similar posts

https://www.airpair.com/python/posts/djangoflaskpyramid

flask (/posts/tag/flask)

pyramid (/posts/tag/pyramid)

ALL POSTS (/POSTS)

17/18

9/19/2015

DjangovsFlaskvsPyramid:ChoosingaPythonWebFramework

You don't realize your code


sucks. Travis and Scrutinizer
FTW.

Using Python and QGIS for


geospatial visualizations - a
Case Study

Jorge Colon

Karolina Alexiou

(https://www.airpair.com/continuousintegration/posts/your-code-sucks-travisand-scrutinizer-ftw)

All you need to know about


servers as a python developer.

Nupur Gulalkari

(https://www.airpair.com/python/posts/usingpython-and-qgis-for-geospatial(https://www.airpair.com/python/posts/pythonvisualization)
servers)

https://www.airpair.com/python/posts/djangoflaskpyramid

18/18

Você também pode gostar