Here's the text of the pdf document, courtesy one of my websites, YAKiToMe! http://www.yakitome.com.
--------------------------- web frameworks design comparison draft - please help me improve it focus on Model-View-Controller frameworks Controllers In Rails class MyTestController < ApplicationController def index render_text "Hello World" end end The name of the class has to match the name of the controller file. Controllers In Django from django.http import HttpResponse def index(request): return HttpResponse("Hello World") Django is explicit, you need to import all functions you use. Controllers In Cherrypy and TurboGears 1.0 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 class MyTestController < ApplicationController def index render_text "Hello "+params[:who] end end 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 from django.http import HttpResponse 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 Similar to Django. All request data is in one place. You can use .get_vars and .post_vars instead of .vars to be more specific. Dispatching In Rails URL http://hostname/MyTest/index gets mapped into class MyTestController < ApplicationController def index render_text "Hello World" end end 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. This can be changed by configuring routes. Dispatching In Django you need to edit url.py to map URLs into actions from django.conf.urls.defaults import * urlpatterns = patterns(’’, (r’^index $’, myapp.mycontroller.index), ) This is the equivalent of Rails’ routes and it requires using regular expressions. 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 class MyTestController < ApplicationController def index @message="Hello World" end end 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 from django.shortcuts import render_to_response 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. You get an error message if the view is not defined. Notice that in Django a view is called a template and a controller is called a view. Calling Views In TurboGears 1.0 with Cherrypy import turbogears from turbogears import controllers, expose class MyRoot(controllers.RootController): @expose(template="MyApp.MyRoot.index") def index(self): return dict(message="Hello World") The view is specified in the expose decorator. Calling Views In web2py 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. The default can be overwritten with response.view=’filename.html’ Views In Rails <% @recipes.each do |recipe| %> <%= recipe.name %> <% end %> It allows full Ruby in views but: - it does not escape strings by default (unsafe) - <% %> requires a special editor since < > are special in HTML Views In Django {% for recipe in recipes %} {{recipe.name}} {% endfor %} The choice of {% %} and {{ }} tags is good because any HTML editor can deal with them. 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 ... 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 {{for recipe in recipes:}}> {{=recipe.name}} {{pass}} 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> The double %% indicate the text has to be escaped. This is off by default, hence unsafe. Should be the opposite. Escaping in Views In Django {% 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 Text is escaped by default. If text should not be escaped it has to be marked with XML Escaping in Views In web2py {{=XML(recipe.name,sanitize=False)}} Text is escaped by default. If text should not be escaped it has to be marked with XML. The optional sanitize option perform XML sanitization by selective escaping some tags and not others. Which tags have to be escaped and which tag attributes can be specified via arguments of XML. Views Hierarchy In Rails Layout Example <%= yield %> 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 ... %> Notice that also :layout follow a naming convention and there is a default. Views Hierarchy In Django Layout Example {% block main %} {% endblock %} 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 ... Views Hierarchy In web2py Layout Example {{include}} 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 <%= form_tag :action => "update" dp %> Name: <%= text_field "item", "name" %> Value: <%= text_field "item", "value" %> <%= submit_tag %> <%= end %> 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. There is a mechanism to validate submitted forms. Forms In Django # 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,}) In Django, you can create a Form (ArticleForm) from a model (Article). The Form knows how to serialize itself and validate the input on selfsubmission, but the errors are not automatically inserted in the form. Forms Kid or Genshi in TurboGears 1.0 or Cherrypy ? I believe you need to use a library like ToscaWidgets. Sorry, I am not familiar with them. If you know how to fill this page please let me know. Forms In web2py 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). Web2py forms can also prevent double submission. Validation In Rails ActiveForm::Definition::create :article do |f| f.section :details do |s| s.text_element :email,:class => ’required’ do |e| e.validates_as_email :msg => ’not email’ end end 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 from django.core.validators import * 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() Similar to Django and Rails because validators are attached to table fields and form fields but validators are classes not objects. This means they must be instantiated with (). You can pass arguments to the validators to change their behavior (for example the error message). The presence of validators affects the way a field is rendered in a form. For example IS_IN_SET() renders the field with a dropbox. Models and Migrations In Rails class Article < ActiveRecord::Migration def self.up create_table :articles do |t| t.column :name, :string t.column :description, :text end end end In Rails there is a place to define tables that need to be created/ deleted/ altered (migrations) and a different place to establish relations between tables (one to many, many to many, etc) Models and Migrations In Django 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) from turbogears.database import metadata, mapper sqlalchemy import Table, Column, Integer mytable = Table(’mytable’, metadata, Column(’id’, Integer, primary_key=True)) class MyTable(object): pass mapper(MyTable, mytable) SQLAlchemy makes a distinction between what tables are in the database and how they are mapped into Python objects. This is because SQLAchemy can deal with legacy databases. Rails, Django and web2py can but with limitations (in the case of web2py for example, tables must have an integer auto-increment key field called "id"). Models and Migrations In web2py Article=db.define_table(`Article’, SQLField(`email’,’string’), SQLField(`description’,’text’) The syntax is a little different but the functionality is similar to Rails. If the the table in the model above does not exist it is created. If the model changes, web2py alters the table accordingly. One to many ad many to many relations are implied by reference fields. Select Query In Rails 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’) "id__gt=3" reads "id greater than 3". Django queries are lazy- evaluated. Select Query In SQLAlchemy (used in TG and Pylons) query(Article).filter_by(id>3, name=’test’) Select Query In web2py db(Article.id>3 and Article.name==’test’).select() In web2py you always need to specify which db you are acting on because you can have multiple db connections in the same code. Transcations In Rails class MyTestController < ApplicationController def index Record.transaction do ... end end end Transactions In Django from django.http import HttpResponse from django.db import transaction @transaction.commit_on_success def index(request): ... return HttpResponse("Hello World") There are multiple decorators: autocommit (commits always), commit_on_success (commit only if not Exception), commit_manually (requires calling) transaction.commit() or transaction.rollback() Transactions In TurboGears import turbogears from turbogears import controllers, expose class MyRoot(controllers.RootController): @expose(template="MyApp.MyRoot.index") def index(self): ... return dict(message="Hello World") By default all actions are enclosed in a transaction that commits on success and rollsback on exception. Transactions In web2py def index() ... return dict(message="Hello World") By default all actions are enclosed in a transaction that commits on success and rollsback on exception. Internationalization In Rails Rails currently doesn’t offer any explicit support for internationalization. Perhaps it should, perhaps it’s too app specific to generalize. http://wiki.rubyonrails.org/rails/pages/Internationalization Thinks will change in Rails 2.2 but here we talk about present, not future. Internationalization In Django from django.http import HttpResponse from django.utils.translation import ugettext as _ def my_view(request): message = _("Hello World") return HttpResponse(message) Using `_’ is the common convention. Requires a few shell commands to build and edit the dictionaries. Internationalization In TurboGears 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) As in the case of Django, this requires some shell commands to build and edit the dictionaries. web2py vs Other Web Frameworks Other Web Frameworks? · j2ee · PHP · CakePHP · Django · Pylons · Ruby on Rails (RoR) Who’s not in the list? · TurboGears (because TG2 is not that different from Pylons+SQLAlchemy +Genshi) · web.py, werkzeug, karrigell, psp, etc. (all excellent frameworks with their functionalities are too limited for a fair comparison) Who’s not in the list? · Cherrypy (the cherrypy wsgiserver is included in web2py) · j2ee (there are too many to choose) · Zope (sorry, I do not understand Zope) Underlying Language web2py j2ee PHP CakePHP Django Pylons RoR python java syntax draws upon C, Java, and Perl php python python ruby Model View Controller web2py j2ee PHP CakePHP Django Pylons RoR yes yes no yes yes yes yes Model View Controller · in web2py, given a model, the default controller appadmin.py provides a database administrative interface (each app has its own) default, has a generic view · in web2py, every controller function, by Model View Controller Web Based Interface web2py j2ee PHP CakePHP Django Pylons RoR yes no no no no no no Django has a database administrative interface only not not an app development/management administrative interface like web2py. only at Heroku.com which, anyway, is very limited compared to the web2py one. Web Based Interface · The web2py web based administrative · The use of the web based interface is interface allows you to do development, debugging, testing, deployment, maintenance, and database administration. "optional" and not required. The same functionality can be accessed via the Python shell. Web Based Interface Web Based Interface web2py j2ee PHP CakePHP Django Pylons RoR Web Based Database Administrative Interface yes no no no yes no no one for every app via third party application via third party application via third party application via third party application via third party application web2py j2ee PHP CakePHP Django Pylons RoR Generic CRUD helpers/controllers yes no no yes yes no yes upload forms · Only web2py and Django have a standard securely renamed, stored on disk and the name is store in the database. Upload is always done via streaming in order to handle large files. mechanism to handle file upload and secure storage. · In case of the web2py the uploaded file is Byte Code Compilation web2py j2ee PHP CakePHP Django Pylons RoR yes yes no no yes yes no there is a button [compile all] it compiles models, controllers and views it is always possible to bytecode compile python code, usually not the views/templates, but this is not as trivial as clicking on one button Byte Code Compilation · web2py and j2ee are the only frameworks that allow to byte code compile applications and distribute them in closed source. Ticketing System web2py j2ee PHP CakePHP Django Pylons RoR yes no no no no no no can be configured to send you an email in case of error can be configured to send you an email in case of error 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. the administrative interface · Administrator can browse and read logs via Zero Installation web2py j2ee PHP CakePHP Django Pylons RoR yes no no no no no no Zero Installation · The binary distributions of web2py (for Windows and Mac) package the interpreter, the SQLite database and the administrative interface. a USB drive. · They require no installation and can run off Zero Configuration web2py j2ee PHP CakePHP Django Pylons RoR yes no no no no no no Zero Configuration · web2py has no configuration file at the framework level. This ensures an easy setup and portability of applications. All other frameworks require some type of configuration. files. · web2py applications can have configuration Web Based Model Designer web2py j2ee PHP CakePHP Django Pylons RoR yes no no no no yes no on web page via CatWalk (SQLObjects only?) Web Based Model Designer Web Based Testing web2py j2ee PHP CakePHP Django Pylons RoR yes no no no no no no as web interface to DocTests Web Based Testing Runs on Google App Engine web2py j2ee PHP CakePHP Django Pylons RoR yes with some limitations no no no yes but not the ORM yes but not all its components 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). web2py database abstraction layer supports the Google Query Language. · No need to rewrite the model since the Caching web2py j2ee PHP CakePHP Django Pylons RoR yes yes yes yes yes yes yes for any function, you can specify whether to cache in ram, on disk, with memcache, or combinations. with third party components memcache memcache ram, disk, db, memcache ram, disk, db, memcache memcache Native Template Language web2py j2ee PHP CakePHP Django Pylons RoR yes yes yes yes yes yes yes 100% Python with no indentation need most common are XML or JSP PHP is itself a template language PHP Django Template Language Kid, Genshi, Mako, Cheetah, etc. Ruby Native Template Language · Any Python Framework can use any Python-based Template Language (for example web2py can use Genshi, Pylons can use web2py’s). consists of pure code embedded in {{ }} tags inside HTML. Blocks end with "pass", so no indentation requirements. · The native web2py template language Template Language · web2py View Example: {{for i in range(10):}} Hello number {{=i}} {{pass}} Template Extension web2py j2ee PHP CakePHP Django Pylons RoR yes yes yes yes yes yes yes Template Extension · web2py Example: {{extend `layout.html’}} Hello world {{include `sidebar.html’}} HTML Helpers web2py j2ee PHP CakePHP Django Pylons RoR yes no no yes yes yes yes Internationalization web2py j2ee PHP CakePHP Django Pylons RoR yes no no yes yes yes no Internationalization · In web2py, text is marked for translation using T("hello world"). · Translations are edited via the provided administrative interface. translations like in · It is possible to have variables in T("Hello %(name)s",dict(name="Massimo")) Database Abstraction web2py j2ee PHP CakePHP Django Pylons RoR yes no no no yes yes yes limited to JavaBeans PearDB does not count because because it requires the developer to write SQL queries and has no Object Relational Mapper via SQLAlchemy or SQLObjects via ActiveRecords Database Abstraction · The web2py ORM works seamlessly with SQLite, MySQL, PostgreSQL, Oracle and on the Google App Engine (with the limitations imposed by the Google system) Database Abstraction · web2py example rows=db(db.user.birthday.year()>1950).select() · equivalent Django example rows=User.objects.filter(birthday__year__gt=1950) Left Outer Joins web2py j2ee PHP CakePHP Django Pylons RoR yes n/a because no ORM n/a because no ORM n/a because no ORM yes yes yes requires a custom Q object with SQLAlchemy, no with SQLObjects Left Outer Joins · All the Python ORMs have the ability to execute raw SQL therefore they allow allow LEFT OUTER JOIN although not in a SQL-dialect independent way Automatic Migrations web2py j2ee PHP CakePHP Django Pylons RoR yes no no no no yes yes Automatic Migrations · In web2py if one changes the data model, it automatically and transparently generates and executes SQL to ALTER TABLEs. There is no special command to type like in Rails. Multiple Databases web2py j2ee PHP CakePHP Django Pylons RoR yes yes yes yes no yes ? but there is a branch that allows it Multiple Databases · In web2py table objects are attributes of a database connection therefore there is no conflict if one establishes multiple connections. · In other framework tables are represented by classes and there may be conflicts if two databases have tables with same name. Distributed Transactions web2py j2ee PHP CakePHP Django Pylons RoR yes yes no no no no no with PostgreSQL only CRUD methods web2py j2ee PHP CakePHP Django Pylons RoR yes no no yes yes no yes via third party plugin via third party plugin Blocks SQL Injections web2py j2ee PHP CakePHP Django Pylons RoR yes no no no yes yes yes up to the programmer to write secure code Blocks Double Submit web2py j2ee PHP CakePHP Django Pylons RoR yes no no no no no no Blocks Double Submit · web2py provides methods to generate forms form database tables and automatically validates and processes forms on submission. It also injects a one-time token in each form to prevent double form submission and some reply attacks. xmlrpc services web2py j2ee PHP CakePHP Django Pylons RoR yes yes no no no yes no Included Ajax Library web2py j2ee PHP CakePHP Django Pylons RoR yes no no yes no no yes jQuery jQuery Scriptaculous Included Ajax Library · Any of the frameworks can use any third party Ajax libraries, here we are concerned with server-side programming only. JSON support web2py j2ee PHP CakePHP Django Pylons RoR yes yes yes yes yes yes yes simplejson is included simplejson is included simplejson is included File Streaming web2py j2ee PHP CakePHP Django Pylons RoR yes yes yes yes yes yes yes by default for all static large files not by default IF_MODIFIED_SINCE web2py j2ee PHP CakePHP Django Pylons RoR yes no no no no yes no by default not out of the box rely on web server for static content, requires programming otherwise 206 PARTIAL CONTENT web2py j2ee PHP CakePHP Django Pylons RoR yes no no no no yes no by default not out of the box rely on web server for static content, requires programming otherwise Handlers for Web Servers · web2py is wsgi compliant. · comes with the cherrypy wsgi fast and sslenbled 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 j2ee PHP CakePHP Django Pylons RoR yes no no yes yes yes yes including reversed, with or without regex allows IP filtering delegated to web server delegated to web server no reversed, no IP filter uses regex, no reversed routes, no IP filter similar to rails reversed routes? no IP filter Routes · In web2py routes are "optional" and there is a default mapping between URLs and controllers (similar to Rails). · IP filter is a web2py feature that allows to map URLs into controllers in a way that depends on the visitor IP pattern. In this way different visitors see different pages but the same URL. Documentation web2py j2ee PHP CakePHP Django Pylons RoR 120 pages draft manual online, one book too many published books many published books online examples only three books online examples only many published books Documentation · Most of the frameworks, including web2py, have extensive online documentation · Web2py also has a repository of free plug- in applications including wikis, blogs, a chat line, an online store, log analyzer, and more. Links to web2py · http://mdp.cti.depaul.edu · FAQ: http://mdp.cti.depaul.edu/AlterEgo · Free Apps: http://mdp.cti.depaul.edu/appliances