Django Vs Flask Vs Pyramid - Choosing A Python Web Framework
Django Vs Flask Vs Pyramid - Choosing A Python Web Framework
DjangovsFlaskvsPyramid:ChoosingaPythonWebFramework
(/)
Login (/login)
Posts (/posts)
More
(/visit/lob-150812)
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
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()
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
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
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
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
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
<!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>
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
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
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
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
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
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
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>
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
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)
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))
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
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!')
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():
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>
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>
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'))
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
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.
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)
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?')
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
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')
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
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>
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
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.
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 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>
#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)}
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
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('/')
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)
Reviews (5)
Rating 4.4/5
Did exactly what was expected and did it clearly with example code.
Upvote - Reply
Very useful.
Upvote - Reply
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
https://www.airpair.com/python/posts/djangoflaskpyramid
16/18
9/19/2015
DjangovsFlaskvsPyramid:ChoosingaPythonWebFramework
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)
17/18
9/19/2015
DjangovsFlaskvsPyramid:ChoosingaPythonWebFramework
Jorge Colon
Karolina Alexiou
(https://www.airpair.com/continuousintegration/posts/your-code-sucks-travisand-scrutinizer-ftw)
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