Web2py Vs Others
Web2py Vs Others
design comparison
draft - please help me improve it
focus on Model-View-Controller frameworks
Controllers
In Rails
The name of the class has to match the name of the controller file.
Controllers
In Django
def index(request):
return HttpResponse("Hello World”)
import cherrypy
class MyRoot:
@cherrypy.expose()
def index(self):
return "Hello World"
Cherrypy, Turbogears, and Pylons are also explicit. You need to import
all functions you want to use.
Controllers
In web2py
def index():
return "Hello World"
web2py is similar to Rails. It imports for you all the web2py keyword.
Often, like in this case, you do not need any.
Get/Post requests
In Rails
GET and POST variables are passed via params but other request
parameters (client ip for example) are passed via a different
mechanism.
Get/Post requests
In Django
def index(request):
return HttpResponse("Hello World %s” % \
request.REQUEST[‘who’])
Nice, simple. The request contains all the info. You can use .GET
or .POST instead of .REQUEST to be more specific.
Get/Post requests
In Cherrypy and TurboGears 1.0
import cherrypy
class MyRoot:
@cherrypy.expose()
def index(self,who):
return "Hello %s" % who
GET and POST variables are passed via arguments of the action, but
other request parameters (client ip for example) are passed via a
different mechanism.
Get/Post requests
In web2py
def index():
return "Hello %s" % request.vars.who
By default Rails does not allow running multiple apps without running
multiple copies of Rails, since the name of the app is not in the URL,
only the controller name (MyTest) and the action name (index) appear.
urlpatterns = patterns('',
(r'^index$', myapp.mycontroller.index),
)
There is no default. You need one entry in url.py for every action.
Dispatching
In Cherrypy and TurboGears 1.0
import cherrypy
class MyRoot:
@cherrypy.expose()
def index(self,who):
return "Hello %s" % who
Works very much like Rails and default mapping between URL and
action can be overwritten.
Dispatching
In web2py a URL like http://hostname/myapp/mycontroller/index calls
def index():
return "Hello %s" % request.vars.who
Similar to Rails and Charrypy but, by default the URL requires that you
specify the name of the app. This allows web2py to run multiple apps
without using routes.
Web2py has its own version of routes that supports two different
syntaxes (with and without regular expression) to overwrite the mapping
and reverse mapping as well.
Calling Views
In Rails
It calls the default view (MyTest/index) which renders the page. The
variables marked by @ are global vars and are passed to the view.
Notice that if the view is not defined, this results in an error message.
Calling Views
In Django
def index(request):
return render_to_response("index.html”,
{‘message’:’Hello World’})
This is the short way of doing it in Django. You have to specify the view
name “index.html” since there is no default. Parameters are passed via
a dictionary.
import turbogears
from turbogears import controllers, expose
class MyRoot(controllers.RootController):
@expose(template="MyApp.MyRoot.index")
def index(self):
return dict(message=”Hello World”)
def index():
return dict(message=”Hello World”)
The last line works like Cherrypy but by default it looks for a view called
“mycontroller/index.html” in “myapp”. If this view does not exist it uses a
generic view to show all variables returned in the dictionary.
<table>
<% @recipes.each do |recipe| %>
<tr>
<td><%= recipe.name %></td>
</tr>
<% end %>
</table>
- <% %> requires a special editor since < > are special in HTML
Views
In Django
<table>
{% for recipe in recipes %}
<tr>
<td>{{recipe.name}}</td>
</tr>
{% endfor %}
</table>
The code looks like Python code but it is not (notice the “endfor” which
is not a python keyword. This limits what you can do in views.
Views
Kid or Genshi in TurboGears 1.0 or Cherrypy
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://purl.org/kid/ns#">
...
<table>
<tr py:for="recipe in recipes" >
<td py:content=”recipe.name”>
</td>
</tr>
</table>
This allows full Python quotes py:for=”...” but it can only be used to
generate HTML/XML views, not dynamical JavaScript for example.
Views
In web2py
<table>
{{for recipe in recipes:}}>
<tr>
<td>{{=recipe.name}}</td>
</tr>
{{pass}}
</table>
Similar to Django but full Python in the code (notice “pass” is a Python
keyword) without indentation requirements (web2py indents the code for
you at runtime). Only one type of escape sequence {{ }} which is
transparent to all HTML editors. All string are escaped (unless otherwise
specified, like in Django and Kid). It can be used to generate JavaScript
(like Django and Rails).
Escaping in Views
In Rails
<%%= message>
{% filter safe %}
{{ message }}
{% endfilter %}
Since Django 1.0 all text is escaped by default. You mark it as safe if
you do not want it to be escaped.
Escaping in Views
Kid or Genshi in TurboGears 1.0 or Cherrypy
<div py:content=”XML(recipe.name)”></div>
{{=XML(recipe.name,sanitize=False)}}
<title>Layout Example</title>
<body>
<%= yield %>
</body>
</html>
and in controller:
render :layout=’filename.html.erb’
The rendered page is inserted in the <%= yield %> tag in the layout.
One can include other views with <%= render ... %>
<title>Layout Example</title>
</head>
<body>
{% block main %} {% endblock %}
</body>
</html>
and in view:
{%block main%}body{%endblock%}
Views can be extended and included using blocks that have names.
Views Hierarchy
Kid or Genshi in TurboGears 1.0 or Cherrypy
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://purl.org/kid/ns#"
py:extends="'master.kid'">
...
Views Hierarchy
In web2py
<title>Layout Example</title>
<body>
{{include}}
</body>
</html>
and in view:
{{extend ‘layout.html’}}
body
Notation similar to Rails but called like in Kid. The body replaces
{{include}} in the layout. layouts can extend other layouts. Views can
include other views.
Forms
In Rails
Rails has helpers to create forms but that’s it. As far as I know there is
no standard mechanism to automatically create forms from models
(database tables). Perhaps there are Rails add-on to do this.
# in model
class ArticleForm(ModelForm):
class Meta:
model = Article
# in controller
def contact(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
return HttpResponseRedirect('/thanks/')
else:
form = ContactForm() # An unbound form
return render_to_response('contact.html', {
'form': form,})
The Form knows how to serialize itself and validate the input on self-
submission, but the errors are not automatically inserted in the form.
Forms
Kid or Genshi in TurboGears 1.0 or Cherrypy
def contact(request):
form = SQLFORM(Article)
if form.accepts(request.vars):
redirect('thanks')
return dict(form=form)
This is the same as the previous Django form (generated from the
Article) model, except that when the form is serialized, if there are
errors, they are displayed in the form itself (unless specified otherwise).
Rails defines validators like requires, email, etc. You can associate
validators to a form. In your controllers you need to check if a form is
valid, and, on error, alter the page to include the errors generated by
validation.
Validation
In Django
class Article(models.Model):
email = models.EmailField(
validator_list=[isValidEmail])
Very much like Rails but more validators. Validators are specified in
models and/or forms.
Validation
In web2py
db.define_table(‘Article’,SQLField(‘email’))
db.Article.email.requires=IS_EMAIL()
class Article(models.Model):
name = models.StringField()
description = models.TextField()
In Django there is one place where models are defined. If the tables do
not exist they are created. Django does not do migrations (i.e. it does
not alter or drop tables if the models change). For many to many
relations, it creates the intermediate link table for you.
Models and Migrations
In SQLAlchemy (used in TG and Pylons)
mapper(MyTable, mytable)
Article=db.define_table(‘Article’,
SQLField(‘email’,’string’),
SQLField(‘description’,’text’)
Article.find(:first,:conditions => [
"id > :id AND name = :name",
{:id => 3, :name => "test" }])
This is the most common notation for a select. The conditions argument
is basically a SQL statement but the parameters are passed as
additional arguments.
Select Query
In Django
Article.objects.filter(id__gt=3,name=’test’)
query(Article).filter_by(id>3, name=’test’)
Select Query
In web2py
@transaction.commit_on_success
def index(request):
...
return HttpResponse("Hello World”)
import turbogears
from turbogears import controllers, expose
class MyRoot(controllers.RootController):
@expose(template="MyApp.MyRoot.index")
def index(self):
...
return dict(message=”Hello World”)
def index()
...
return dict(message=”Hello World”)
Thinks will change in Rails 2.2 but here we talk about present, not
future.
Internationalization
In Django
def my_view(request):
message = _("Hello World")
return HttpResponse(message)
import turbogears
from turbogears import controllers, expose
class MyRoot(controllers.RootController):
@expose(template="MyApp.MyRoot.index")
def index(self):
message=_(“Hello World”)
return dict(message=message)
j2ee java
CakePHP php
Django python
Pylons python
RoR ruby
Model View Controller
web2py yes
j2ee yes
PHP no
CakePHP yes
Django yes
Pylons yes
RoR yes
Model View Controller
Pylons no
RoR no only at Heroku.com which, anyway, is very limited
compared to the web2py one.
Web Based Interface
• The web2py web based administrative
interface allows you to do development,
debugging, testing, deployment,
maintenance, and database administration.
• The use of the web based interface is
“optional” and not required. The same
functionality can be accessed via the Python
shell.
Web Based Interface
Web Based Interface
Web Based Database
Administrative Interface
web2py yes one for every app
Django yes
Pylons no via third party application
j2ee yes
PHP no
CakePHP no
Django yes it is always possible to bytecode compile python
code, usually not the views/templates, but this is
Pylons yes not as trivial as clicking on one button
RoR no
Byte Code Compilation
RoR no
Ticketing System
• web2py has not distinction between
debugging and production modes. All
uncaught exceptions are logged and a ticket
is issued to the visitor in order to recover
the associated log.
• Administrator can browse and read logs via
the administrative interface
Zero Installation
web2py yes
j2ee no
PHP no
CakePHP no
Django no
Pylons no
RoR no
Zero Installation
j2ee no
PHP no
CakePHP no
Django no
Pylons yes via CatWalk (SQLObjects only?)
RoR no
Web Based Model Designer
Web Based Testing
web2py yes as web interface to DocTests
j2ee no
PHP no
CakePHP no
Django no
Pylons no
RoR no
Web Based Testing
Runs on Google App Engine
web2py yes with some limitations
j2ee no
PHP no
CakePHP no
Django yes but not the ORM
Pylons yes but not all its components
RoR no
Runs on Google App Engine
• web2py is the only framework that allows
to develop on your own platform and then
run the app, unmodified on the Google App
Engine (with the limitations imposed by the
App Engine).
• No need to rewrite the model since the
web2py database abstraction layer supports
the Google Query Language.
Caching
web2py yes for any function, you can specify whether to cache
in ram, on disk, with memcache, or combinations.
{{for i in range(10):}}
{{pass}}
</body></html>
Template Extension
web2py yes
j2ee yes
PHP yes
CakePHP yes
Django yes
Pylons yes
RoR yes
Template Extension
• web2py Example:
{{extend ‘layout.html’}}
<h1>Hello world</h1>
{{include ‘sidebar.html’}}
HTML Helpers
web2py yes
j2ee no
PHP no
CakePHP yes
Django yes
Pylons yes
RoR yes
Internationalization
web2py yes
j2ee no
PHP no
CakePHP yes
Django yes
Pylons yes
RoR no
Internationalization
PHP no PearDB does not count because because it requires the developer
to write SQL queries and has no Object Relational Mapper
CakePHP no
Django yes
Pylons yes via SQLAlchemy or SQLObjects
• web2py example
rows=db(db.user.birthday.year()>1950).select()
RoR yes
Left Outer Joins
Pylons yes
RoR ?
Multiple Databases
j2ee yes
PHP no
CakePHP no
Django no
Pylons no
RoR no
CRUD methods
web2py yes
j2ee no via third party plugin
CakePHP yes
Django yes
Pylons no
RoR yes
Blocks SQL Injections
web2py yes
j2ee no
up to the programmer
PHP no to write secure code
CakePHP no
Django yes
Pylons yes
RoR yes
Blocks Double Submit
web2py yes
j2ee no
PHP no
CakePHP no
Django no
Pylons no
RoR no
Blocks Double Submit
j2ee yes
PHP yes
CakePHP yes
not by default
Django yes
Pylons yes
RoR yes
IF_MODIFIED_SINCE
web2py yes by default
j2ee no
PHP no
CakePHP no not out of the box
rely on web server for static content,
Django no requires programming otherwise
Pylons yes
RoR no
206 PARTIAL CONTENT
web2py yes by default
j2ee no
PHP no
not out of the box
CakePHP no rely on web server for static content,
requires programming
Django no otherwise
Pylons yes
RoR no
Handlers for Web Servers
• web2py is wsgi compliant.
• comes with the cherrypy wsgi fast and ssl-
enbled web server.
• runs with apache and mod_proxy or
mod_rewrite or mod_wsgi.
• runs with lightpd with FastCGI.
• runs as CGI script.
Routes
web2py yes including reversed, with or without regex
allows IP filtering
• http://mdp.cti.depaul.edu
• FAQ: http://mdp.cti.depaul.edu/AlterEgo
• Free Apps: http://mdp.cti.depaul.edu/appliances