from Django to web2py
[edit|delete]Disclaimer
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.
web2py was stable from day one, it is now version 1.32 it never broke and it will never break backward compatibility.
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 web2py.py -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
urls.py vs routes.py
Django has urls.py, web2py has routes.py. In web2py the use of routes.py is optional and a path /a/c/f maps into a call to function f() in controller c.py in application a. You only edit routes.py if you want to change the default behavior.
The equivalent of this urls.py 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 routes.py) file:
routes_out=(
('/polls/latest\.php', '/polls/views/index'),
)
In web2py you can also define reverse routes (routes_out) and set routes filters by remote address.
Models
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:
db=SQLDB('sqlite://mydb.db')
db.define_table('poll',
SQLField('question',length=200),
SQLField('pub_date','datetime'))
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?',pub_date=datetime.datetime.now())
p.save()
To insert records in web2py
p=db.poll.insert(question='What's up?',pub_date=datetime.datetime.now())
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:
rows=Poll.object.filter(pub_date__year__gt=2005)
To select some records in web2py:
rows=db(db.poll.pub_date.year()>2005).select()
- 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.
Controllers
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():
latest_poll_list=db().select(db.poll.ALL,orderby=~db.poll.pub_date,limitby=(0,5))
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 controller.py 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
redirect(URL(r=request,f='index'))
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
<html><body>
{% if latest_poll_list %}
<ul>
{% for poll in latest_poll_list %}
<li>{{ poll.question }}</li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}
</body></html>
In web2py the same output could be produced by
{{extend 'layout.html'}}
{{ if len(latest_poll_list): }}
<ul>
{{ for poll in latest_poll_list: }}
<li>{{= poll.question }}</li>
{{ pass }}
</ul>
{{ 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
session.myvariable=3
and you retrieve it with
myvariable=session.myvariable
- 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.
Internationalization
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
it-it.py) in the admin interface.
Helpers
web2py also has helpers to create HTML for example
{{=A('here',_href=URL(r=request,f='index')}}
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.
Forms
Here is how to create a create_poll form in web2py
def create_poll():
form=SQLFORM(db.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.
Validators
Django validators are functions and they are used as in the following example
from django.core.validators import isValidEmail
EmailField(...,validator_list=[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_DBandIS_NOT_IN_DBcheck whether the values is or is not already in the database. - If a form field requires that it
IS_IN_DBit is automatically rendered as a select/option input field.
Tests
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.
Cache
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
@cache_page(60*15)
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:
@cache(request.env.path_info,time_expire=60*15,cache_model=cache.ram)
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
memcache_servers=['127.0.0.1:11211']
cache.memcache=MemcacheClient(request,memcache_servers)
and then use cache.memcache in place of cache.ram.
Ajax
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.