Model-View-Controller

web2py encourages the developer to separate data representation (the model), data presentation (the view) and the application workflow (the controller). Let’s consider again the previous example and see how to build a web2py application around it. Here is an example of the web2py MVC edit interface:

image

The typical workflow of a request in web2py is described in the following diagram:

image

In the diagram:

  • The Server can be the web2py built-in web server or a third-party server, such as Apache. The Server handles multi-threading.
  • “main” is the main WSGI application. It performs all common tasks and wraps user applications. It deals with cookies, sessions, transactions, URL routing and reverse routing, and dispatching.

It can serve and stream static files if the web server is not doing it yet.

  • The Models, Views and Controller components make up the user application.
  • Multiple applications can be hosted in the same web2py instance.
  • The dashed arrows represent communication with the database engine(s). The database queries can be written in raw SQL (discouraged) or by using the web2py Database Abstraction Layer (recommended), so that web2py application code is not dependent on the specific database engine.
  • The dispatcher maps the requested URL to a function call in the controller. The output of the function can be a string or a dictionary of symbols (a hash table). The data in the dictionary is rendered by a view. If the visitor requests an HTML page (the default), the dictionary is rendered into an HTML page. If the visitor requests the same page in XML, web2py tries to find a view that can render the dictionary in XML. The developer can create views to render pages in any of the already supported protocols (HTML, XML, JSON, RSS, CSV, and RTF) or in additional custom protocols.
  • All calls are wrapped into a transaction, and any uncaught exception causes the transaction to be rolled back. If the request succeeds, the transaction is committed.
  • web2py also handles sessions and session cookies automatically, and when a transaction is committed, the session is also stored, unless specified otherwise.
  • It is possible to register recurrent tasks (via cron) to run at scheduled times and/or after the completion of certain actions. In this way it is possible to run long and compute-intensive tasks in the background without slowing down navigation.

Here is a minimal and complete MVC application, consisting of three files:

“db.py” is the model: A very simple web2py app would define a database connection like so:

  1. db = DAL('sqlite://storage.sqlite')
  2. db.define_table('contact',
  3. Field('name'),
  4. Field('phone'))

It connects to the database (in this example a SQLite database stored in the storage.sqlite file) and defines a table called contact. If the table does not exist, web2py creates it and, transparently and in the background, generates SQL code in the appropriate SQL dialect for the specific database engine used. The developer can see the generated SQL but does not need to change the code if the database back-end, which defaults to SQLite, is replaced with MySQL, PostgreSQL, MSSQL, FireBird, Oracle, DB2, Informix, Interbase, Ingres, and the Google App Engine (both SQL and NoSQL).

Note that modern versions of web2py leave the specific database connection to a configuration text file stored in the application’s private directory. This makes it easier to deploy the application from a development environment to a production server because there is almost certainly a different database connection. So, instead of

  1. db = DAL('sqlite://storage.sqlite')

You will see code like this:

  1. ## app configuration made easy. Look inside private/appconfig.ini
  2. from gluon.contrib.appconfig import AppConfig
  3. ## once in production, remove reload=True to gain full speed
  4. myconf = AppConfig(reload=True)
  5. ...
  6. db = DAL(myconf.take('db.uri'), pool_size=myconf.take('db.pool_size', cast=int), check_reserved=['all'])

If you look at the configuration textfile private/appconfig.ini you will see that it still defaults to sqlite.

Once a table is defined and created, web2py also generates a fully functional web-based database administration interface, called appadmin, to access the database and the tables.

“default.py” is the controller:

  1. def contacts():
  2. grid=SQLFORM.grid(db.contact, user_signature=False)
  3. return locals()

In web2py, URLs are mapped to Python modules and function calls. In this case, the controller contains a single function (or “action”) called contacts. An action may return a string (the returned web page) or a Python dictionary (a set of key:value pairs) or the set of local variables (as in this example). If the function returns a dictionary, it is passed to a view with the same name as the controller/function, which in turn renders the page. In this example, the function contacts generates a select/search/create/update/delete grid for table db.contact and returns the grid to the view.

“default/contacts.html” is the view:

  1. {{extend 'layout.html'}}
  2. <h1>Manage My Contacts</h1>
  3. {{=grid}}

This view is called automatically by web2py after the associated controller function (action) is executed. The purpose of this view is to render the variables in the returned dictionary (in our case grid) into HTML. The view file is written in HTML, but it embeds Python code delimited by the special {{ and }} delimiters. This is quite different from the PHP code example, because the only code embedded into the HTML is “presentation layer” code. The “layout.html” file referenced at the top of the view is provided by web2py and constitutes the basic layout for all web2py applications. The layout file can easily be modified or replaced.