Some of the information here may be outdated, please check the book instead


People are trying to hack into this site every day to post inappropriate material, so edit access is blocked. If there is anything incorrect stated about Django here, please email me and it will be fixed. It is my interest to post correct and unbiased information.


  • We really like Django and web2py has taken inspiration from Django.

  • If we say anything wrong about Django please let us know and we will correct it.

  • These are just some tips on how to move a Django project to web2py, it is not a comparison

  • Notice that web2py is not based on Django although it has some similarities in the syntax

  • Many of the Django examples below are from the Django documentation page

Features comparison

  • Click here for a detailed feature comparison between web2py and other frameworks.

  • Click here for a comparison with TG

  • Click here for a comparison with Rails (work in progres)

General observation (test)

Django and web2py are both MVC frameworks but what Django calls a view, web2py calls a controller and what Django calls a template we2py calls a view. We are sorry for the confusion but the web2py naming convention is the standard one.

Shell or not shell

Both Django and web2py have a shell. Web2py also has an administrative interface that allows you create, import, delete, export, design/edit, debug, test, and administer you app, so you do not need to use the shell. Here is a demo web2py also provides an ajax web-based python shell to interact with the apps and their models.

You can start the text-based shell with

python -S admin

You can get the ajax shell and other ready made web2py apps from the repository of appliances

There is also a Ulipad Shell Plugin vs

Django has, web2py has In web2py the use of is optional and a path /a/c/f maps into a call to function f() in controller in application a. You only edit if you want to change the default behavior.

The equivalent of this in Django

from django.conf.urls.defaults import *
urlpatterns = patterns('',
  (r'^polls/latest\.php$', 'mysite.polls.views.index'),

In web2py would more or less be (in the file:

  ('/polls/latest\.php', '/polls/views/index'),

In web2py you can also define reverse routes (routes_out) and set routes filters by remote address.


This is an example of a Django model:

from django.db import models
class Poll(models.Model):
   question = models.CharField(max_length=200)
   pub_date = models.DateTimeField('date published')

This is the same in a web2py model:


Notice that:

  • In web2py you do not need to import the web2py stuff.
  • The connection URI is specified in the model, not in the configuration file because web2py has no configuration files. This enables you to connect to multiple databases within different apps and within the same app.
  • As in Django the few statements above are sufficient to generate an administrative interface for the app.
  • Unlike Django if you change the fields in the table, web2py will do a migration for you and no questions asked.
  • There are no shell commands to type. Just edit the models/view/controllers and things will happen.
  • web2py works with SQLite, MySQL, PostgreSQL, and Oracle.

You can use the interactive model builder for generate web2py models.

To insert records in Django

 p=Poll(question='What's up?',

To insert records in web2py

 p=db.poll.insert(question='What's up?',

Notice that:

  • In web2py there is no save. If the insert is executed in a controller the above statement is executed in a transaction. The transaction is committed if no exception is raised by the controller, else it is rolled back.
  • In web2py db.insert returns the id of the record that was just inserted.
  • Tables are attributes of the object representing the db connection. This is because in web2py you can have multiple connections. You can also do distributed transactions with postgresql.

To select some records in Django:


To select some records in web2py:

  • In web2py the argument of db(...) defines the set. You can use ()&() for AND and ()|() for OR and ~() for NOT.
  • In web2py the condition in db(...) can involve multiple tables and this will result in an automatic join.
  • In web2py you can pass arguments to .select(...) to select only some fields, sort them, group them and cache the select.


The following Django view:

from django.shortcuts import render_to_response
from mysite.polls.models import Poll
from django.http import HttpResponse
def index(request):
    latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
    return render_to_response('polls/index.html', {'latest_poll_list': latest_poll_list})

In web2py would be

def index():
     return dict(latest_poll_list=latest_poll_list)

Notice that:

  • In web2py you do not need to import web2py stuff nor models. If you defined them, web2py understand you plan to use them.
  • You do not need to specify a view. web2py assumes index() in will be rendered by controller/index.html. You can change this default behavior.
  • In Django you need to create the view. In web2py you should but, if you don't, it will render the variables anyway using a generic view.

In Django you raise errors with

from django.http import Http404
raise Http404

In web2py you raise errors with

raise HTTP(404)

and you redirect with


Which redirects the visitor to the 'index' controller function within the same application/controller.

Notice that:

  • In Django all uncaught exceptions other than Http# result in a traceback shown to the visitor (in debug mode) or in an email sent to the administrator (in production)
  • In web2py all uncaught exceptions other than HTTP(...) result are logged and the administrator can browse them via the administrative interface. A ticket is issued to the visitor in order to track the cause of the exception. web2py never shows code to the visitor, not even by mistake.

Views (also known as templates)

In Django the view for the above controller function index() would look like

{% if latest_poll_list %}
    {% for poll in latest_poll_list %}
        <li>{{ poll.question }}</li>
    {% endfor %}
{% else %}
    <p>No polls are available.</p>
{% endif %}

In web2py the same output could be produced by

{{extend 'layout.html'}}
{{ if len(latest_poll_list): }}
    {{ for poll in latest_poll_list: }}
        <li>{{= poll.question }}</li>
    {{ pass }}
{{ else: }}
    <p>No polls are available.</p>
{{ pass }}

Notice that:

  • Django has it own template language.
  • web2py uses Python in {{ }} as template language without any limitation.
  • web2py uses "pass" to terminate blocks when it is not obvious.
  • You can build your own layout files as in Django but you can also use our automated layout builder

On request, response, session

In web2py form variables are in request.vars and they can be accessed by

request.var.myvariable or request.vars['myvariable']

The first notation returns None if not request.vars.has_key('myvariable'). The second raises an exception. You can use request.post_vars to get only post vars and request.get_vars to get only get variables.

To store stuff in session you do


and you retrieve it with

  • Normally sesions are stored on disk and locked when in use.
  • It is also possible to store session on database.
  • sessions are saved automatically when web2py returns, unless specified otherwise.
  • It is possible for multiple apps to cooperate by sharing sessions and/or databases

The response object is used to stream data, create xmlrpc services and send some standard variables to the views.


In Django:

from django.utils.translation import ugettext as _
_('this is a message')

In web2py:

T('this is a message')

(and as usual no need to import anything since you are supposed to use T) You web2py you can also do

T('this is message %(name)s',dict(name='xxx'))
  • In both cases the expression is evaluated lazily when displayed in views.
  • In web2py, the web based administrative interface provides a translation page interface.
  • To create a language file in web2py just type its name (for example ) in the admin interface.


web2py also has helpers to create HTML for example


produces the same output as

<a href="{{=URL(r=request,f='index)}}">here</a>

Helpers can be nested as in

HTML(BODY(H1('title'),P('bla '*10,_id='main')))

and they can be used to build forms via FORM and INPUT.

Notice that web2py escapes all text displayed in views to prevent XSS. Only text marked by XML('...') is not escaped.


Here is how to create a create_poll form in web2py

 def create_poll():
     if form.accepts(request.vars): response.flash='poll posted'
     elif form.errors: response.flash='there are errors!'
     return dict(form=form)

we could not figure out how to do this concisely in Django so we did not list it here.

Notice that in we2bpy:

  • SQLFORM can generate Create/Update/delete forms
  • SQLFORM.accepts does all the processing of input variables, validation and database IO. If the form is validated it performs the database insert or update. If it is not validated it alters the form and includes error messages that are displayed with the form.


Django validators are functions and they are used as in the following example

from django.core.validators import isValidEmail

web2py validators are objects which means they take parameters (for example the error message on failed validation)

db.mytable.myfield.requires=[IS_EMAIL(error_message="invalid email")]
  • You never need to call web2py validators since they are called automatically upon form submission.
  • The error messages are automatically inserted in forms.
  • Some validators like IS_IN_DB and IS_NOT_IN_DB check whether the values is or is not already in the database.
  • If a form field requires that it IS_IN_DB it is automatically rendered as a select/option input field.


Both Django and web2py use doctests for testing.

In web2py, the administrative interface provides a button to run all the tests for you app and writes a report.


In Django you need to setup the cache at the framework level and then use it to store/retrieve objects or cache controllers as in

def index(request): ...

In web2py there is no configuration to do. There are two built-in caching mechanisms cache.ram and cache.disk and other plugins (memcache). You can use them as follows:

def index(): ...

The first argument of cache is the key to be used for caching the function, the second is the expiration time in seconds (same as Django) and the third is the cache type. You need to specify because you can use different caching mechanisms within the same ap. You can also use cache.ram and cache.disk to cache any function, select and view.

If you really like memcache you can define cache.memcache by doing

from gluon.contrib.memcache import MemcacheClient

and then use cache.memcache in place of cache.ram.


web2py comes with jQuery base.

Google App Engine

Both Django and web2py run on Google App Engine but, in the case of Django, you have to rewrite your models, your insert and your select to use the app engine API. In the case of web2py most of the ORM functionality will work as is on the app engine.

© 2008-2010 by Massimo Di Pierro - All rights reserved - Powered by web2py - design derived from a theme by the earlybird
The content of this book is released under the Artistic License 2.0 - Modified content cannot be reproduced.