The web2py workflow is the following:
- An HTTP requests arrives to the web server (the built-in Rocket server or a different server connected to web2py via WSGI or another adapter). The web server handles each request in its own thread, in parallel.
- The HTTP request header is parsed and passed to the dispatcher (explained later in this chapter).
- The dispatcher decides which of the installed application will handle the request and maps the PATH_INFO in the URL into a function call. Each URL corresponds to one function call.
- Requests for files in the static folder are handled directly, and large files are automatically streamed to the client.
- Requests for anything but a static file are mapped into an action (i.e. a function in a controller file, in the requested application).
- Before calling the action, a few things happen: if the request header contains a session cookie for the app, the session object is retrieved; if not, a session id is created (but the session file is not saved until later); an execution environment for the request is created; models are executed in this environment.
- Finally the controller action is executed in the pre-built environment.
- If the action returns a string, this is returned to the client (or if the action returns a web2py HTML helper object, it is serialized and returned to the client).
- If the action returns an iterable, this is used to loop and stream the data to the client.
- If the action returns a dictionary, web2py tries to locate a view to render the dictionary. The view must have the same name as the action (unless specified otherwise) and the same extension as the requested page (defaults to .html); on failure, web2py may pick up a generic view (if available and if enabled). The view sees every variable defined in the models as well as those in the dictionary returned by the action, but does not see global variables defined in the controller.
- The entire user code is executed in a single database transaction unless specified otherwise.
- If the user code succeeds, the transaction is committed.
- If the user code fails, the traceback is stored in a ticket, and a ticket ID is issued to the client. Only the system administrator can search and read the tracebacks in tickets.
There are some caveats to keep in mind:
- Models in the same folder/subfolder are executed in alphabetical order.
- Any variable defined in a model will be visible to other models following alphabetically, to the controllers, and to the views.
- Models in subfolders are executed conditionally. For example, if the user has requested "/a/c/f" where "a" is the application, "c" is the controller, and "f" is the function (action), then the following models are executed:
``
applications/a/models/*.py
applications/a/models/c/*.py
applications/a/models/c/f/*.py
``
- The requested controller is executed and the requested function is called. This means all top-level code in the controller is also executed at every request for that controller.
- The view is only called if the action returns a dictionary.
- If a view is not found, web2py tries to use a generic view. By default, generic views are disabled, although the 'welcome' app includes a line in /models/db.py to enable them on localhost only. They can be enabled per extension type and per action (using ``response.generic_patterns``). In general, generic views are a development tool and typically should not be used in production. If you want some actions to use a generic view, list those actions in ``response.generic_patterns`` (discussed in more detail in the chapter on Services).
The possible behaviors of an action are the following:
Notice that web2py validates all URLs to prevent directory traversal attacks.
URLs are only allowed to contain alphanumeric characters, underscores, and slashes; the ``args`` may contain non-consecutive dots. Spaces are replaced by underscores before validation. If the URL syntax is invalid, web2py returns an HTTP 400 error message``http-w``:cite ``http-o``:cite .
If the URL corresponds to a request for a static file, web2py simply reads and returns (streams) the requested file.
If the URL does not request a static file, web2py processes the request in the following order:
- Parses cookies.
- Creates an environment in which to execute the function.
- Initializes ``request``, ``response``, ``cache``.
- Opens the existing ``session`` or creates a new one.
- Executes the models belonging to the requested application.
- Executes the requested controller action function.
- If the function returns a dictionary, executes the associated view.
- On success, commits all open transactions.
- Saves the session.
- Returns an HTTP response.
Notice that the controller and the view are executed in different copies of the same environment; therefore, the view does not see the controller, but it sees the models and it sees the variables returned by the controller action function.
If an exception (other than HTTP) is raised, web2py does the following:
- Stores the traceback in an error file and assigns a ticket number to it.
- Rolls back all open database transactions.
- Returns an error page reporting the ticket number.
If the exception is an ``HTTP`` exception, this is assumed to be the intended behavior (for example, an ``HTTP`` redirect), and all open database transactions are committed. The behavior after that is specified by the ``HTTP`` exception itself. The ``HTTP`` exception class is not a standard Python exception; it is defined by web2py.
### Libraries
The web2py libraries are exposed to the user applications as global objects. For example (``request``, ``response``, ``session``, ``cache``), classes (helpers, validators, DAL API), and functions (``T`` and ``redirect``).
These objects are defined in the following core files:
``
web2py.py
gluon/__init__.py gluon/highlight.py gluon/restricted.py gluon/streamer.py
gluon/admin.py gluon/html.py gluon/rewrite.py gluon/template.py
gluon/cache.py gluon/http.py gluon/rocket.py gluon/storage.py
gluon/cfs.py gluon/import_all.py gluon/sanitizer.py gluon/tools.py
gluon/compileapp.py gluon/languages.py gluon/serializers.py gluon/utils.py
gluon/contenttype.py gluon/main.py gluon/settings.py gluon/validators.py
gluon/dal.py gluon/myregex.py gluon/shell.py gluon/widget.py
gluon/decoder.py gluon/newcron.py gluon/sql.py gluon/winservice.py
gluon/fileutils.py gluon/portalocker.py gluon/sqlhtml.py gluon/xmlrpc.py
gluon/globals.py gluon/reserved_sql_keywords.py
``:code
------
Notice that many of these modules, specifically ``dal`` (the Database Abstraction Layer), ``template`` (the template language), ``rocket`` (the web server), and
``html`` (the helpers) have no dependencies and can be used outside of web2py.
-----
The tar gzipped scaffolding app that ship with web2py is
``
welcome.w2p
``:code
This is created upon installation and overwritten on upgrade.
-------
The first time you start web2py, two new folders are created: deposit and applications. The deposit folder is used as temporary storage for installing and uninstalling applications.
Also, the first time you start web2py and after an upgrade, the "welcome" app is zipped into a "welcome.w2p" file to be used as a scaffolding app.
-------
When web2py is upgraded it comes with a file called "NEWINSTALL". If web2py finds this file, it understands an upgrade was performed, hence it removed the file and creates a new "welcome.w2p".
The current web2py version is stored in the field "VERSION" and it follows standard semantic versioning notation where the build id is the build timestamp.
web2py unit-tests are in
``
gluon/tests/
``:code
There are handlers for connecting with various web servers:
``
cgihandler.py # discouraged
gaehandler.py # for Google App Engine
fcgihandler.py # for FastCGI
wsgihandler.py # for WSGI
isapiwsgihandler.py # for IIS
modpythonhandler.py # deprecated
``:code
("fcgihandler" calls "gluon/contrib/gateways/fcgi.py" developed by Allan Saddi) and
gluon/contrib/rss2.py
``
gluon/contrib/simplejson/
``:code
**Google Wallet** ``googlewallet``:cite
provides "pay now" buttons which link Google as payment processor:
``
gluon/contrib/google_wallet.py
``:code
**Stripe.com** ``stripe``:cite provides a simple API for accepting credit card payments:
``
gluon/contrib/stripe.py
``:code
**AuthorizeNet** ``authorizenet``:cite provides API to accept credit card payments via Authorize.net network
``
gluon/contrib/AuthorizeNet.py
``:code
**Dowcommerce** ``dowcommerce``:cite credit card processing API:
``
gluon/contrib/DowCommerce.py
``:code
**PaymentTech** credit card processing API:
``
gluon/contrib/paymentech.py
``:code
**PAM**``PAM``:cite authentication API created by Chris AtLee:
``
gluon/contrib/pam.py
``:code
A Bayesian classifier to populate the database with dummy data for testing purposes:
``
gluon/contrib/populate.py
``:code
A file with API for running on Heroku.com : ``heroku``:inxx
``
gluon/contrib/heroku.py
``:code
Applications developed in web2py are composed of the following parts:
- **databases** store SQLite databases and additional table information.
- **cache** store cached application items.
- **modules** are other optional Python modules.
- **private** files are accessed by the controllers but not directly by the developer.
- **uploads** files are accessed by the models but not directly by the developer (e.g., files uploaded by users of the application).
- **tests** is a directory for storing test scripts, fixtures and mocks.
Models, views, controllers, languages, and static files are accessible via the web administration [design] interface. ABOUT, README, and errors are also accessible via the administration interface through the corresponding menu items. Sessions, cache, modules and private files are accessible to the applications but not via the administration interface.
Everything is neatly organized in a clear directory structure that is replicated for every installed web2py application, although the user never needs to access the filesystem directly:
``about``:inxx ``license``:inxx ``cache``:inxx ``controllers``:inxx ``databases``:inxx ``errors``:inxx ``languages``:inxx ``models``:inxx ``modules``:inxx ``private``:inxx ``session``:inxx ``static``:inxx ``tests``:inxx ``uploads``:inxx ``views``:inxx ``__init__.py``:inxx
``
__init__.py ABOUT LICENSE models views
controllers modules private tests cron
cache errors upload sessions static
``:code
"__init__.py" is an empty file which is required in order to allow Python (and web2py) to import the modules in the ``modules`` directory.
Notice that the **admin** application simply provides a web interface to web2py applications on the server file system. web2py applications can also be created and developed from the command-line or your preferred text editor/IDE; you don't have to use the browser **admin** interface. A new application can be created manually by replicating the above directory structure under ,e.g., "applications/newapp/" (or simply untar the ``welcome.w2p`` file into your new application directory). Application files can also be created and edited from the command-line without having to use the web **admin** interface.
### API
Models, controllers, and views are executed in an environment where the following objects are already imported for us:
**Global Objects:** ``request``:inxx ``response``:inxx ``session``:inxx ``cache``:inxx
``
request, response, session, cache
``:code
**Internationalization:** ``T``:inxx ``internationalization``:inxx
``
T
``:code
**Navigation:** ``redirect``:inxx ``HTTP``:inxx
``
IS_UPLOAD_FILENAME, IS_UPPER, IS_URL
**Database:** ``DAL``:inxx
``
DAL, Field
``:code
For backward compatibility ``SQLDB=DAL`` and ``SQLField=Field``. We encourage you to use the new syntax ``DAL`` and ``Field``, instead of the old syntax.
Other objects and modules are defined in the libraries, but they are not automatically imported since they are not used as often.
The core API entities in the web2py execution environment are ``request``, ``response``, ``session``, ``cache``, ``URL``, ``HTTP``, ``redirect`` and ``T`` and are discussed below.
A few objects and functions, including **Auth**, **Crud** and **Service**, are defined in "gluon/tools.py" and they need to be imported as necessary:
``
from gluon.tools import Auth, Crud, Service
``:code
#### Accessing the API from Python modules
Your models or controller may import python modules, and these may need to use some of the web2py API. The way to do it is by importing them:
``
from gluon import *
``
In fact, any Python module, even if not imported by a web2py application, can import the web2py API as long as web2py is in the ``sys.path``.
There is one caveat, though. Web2py defines some global objects (request, response, session, cache, T) that can only exist when an HTTP request is present (or is faked). Therefore, modules can access them only if they are called from an application. For this reasons they are placed into a container caller ``current``, which is a thread local object. Here is an example.
Create a module "/myapp/modules/test.py" that contains:
``
from gluon import *
def ip(): return current.request.client
``
Now from a controller in "myapp" you can do
``
import test
def index():
return "Your ip is " + test.ip()
``
Unlike a dictionary, if an attribute (or key) does not exist, it does not raise
-----
It is sometimes useful to create your own Storage objects. You can do so as follows:
``
from gluon.storage import Storage
my_storage = Storage() # empty storage object
my_other_storage = Storage(dict(a=1, b=2)) # convert dictionary to Storage
``:code
-----
``request`` has the following items/attributes, some of which are also an instance of the ``Storage`` class:
- ``request.cookies``: a ``Cookie.SimpleCookie()`` object containing the cookies passed with the HTTP request. It acts like a dictionary of cookies. Each cookie is a Morsel object (see http://docs.python.org/2/library/cookie.html#id2).
- ``request.env``: a ``Storage`` object containing the environment variables passed to the controller, including HTTP header variables from the HTTP request and standard WSGI parameters. The environment variables are all converted to lower case, and dots are converted to underscores for easier memorization.
- ``request.application``: the name of the requested application (parsed from ``request.env.path_info``).
- ``request.controller``: the name of the requested controller (parsed from the ``request.env.path_info``).
- ``request.function``: the name of the requested function (parsed from the ``request.env.path_info``).
- ``request.extension``: the extension of the requested action. It defaults to "html". If the controller function returns a dictionary and does not specify a view, this is used to determine the extension of the view file that will render the dictionary (parsed from the ``request.env.path_info``).
- ``request.folder``: the application directory. For example if the application is "welcome", ``request.folder`` is set to the absolute path "/path/to/welcome". In your programs, you should always use this variable and the ``os.path.join`` function to build paths to the files you need to access. Although web2py always uses absolute paths, it is a good rule never to explicitly change the current working folder (whatever that is) since this is not a thread-safe practice.
- ``request.now``: a ``datetime.datetime`` object storing the datetime of the current request.
- ``request.utcnow``: a ``datetime.datetime`` object storing the UTC datetime of the current request.
- ``request.args``: A list of the URL path components following the controller function name; equivalent to ``request.env.path_info.split('/')[3:]``
- ``request.vars``: a ``gluon.storage.Storage`` object containing both the HTTP GET and HTTP POST query variables.
- ``request.get_vars``: a ``gluon.storage.Storage`` object containing only the HTTP GET query variables.
- ``request.post_vars``: a ``gluon.storage.Storage`` object containing only the HTTP POST query variables.
- ``request.client``: The ip address of the client as determined by, if present, ``request.env.http_x_forwarded_for`` or by ``request.env.remote_addr`` otherwise. While this is useful it should not be trusted because the ``http_x_forwarded_for`` can be spoofed.
- ``request.is_local``: ``True`` if the client is localhost, ``False`` otherwise. Should work behind a proxy if the proxy supports ``http_x_forwarded_for``.
- ``request.is_https``: ``True`` if the request is using the HTTPS protocol, ``False`` otherwise.
- ``request.body``: a read-only file stream that contains the body of the HTTP request. This is automatically parsed to get the ``request.post_vars`` and then rewinded. It can be read with ``request.body.read()``.
- ``request.ajax`` is True if the function is being called via an Ajax request.
- ``request.cid`` is the ``id`` of the component that generated the Ajax request (if any). You can read more about components in Chapter 12.
- ``request.requires_https()`` prevents further code execution if the request is not over HTTPS and redirects the visitor to the current page over HTTPS.
- ``request.restful`` this is a new and very useful decorator that can be used to change the default behavior of web2py actions by separating GET/POST/PUSH/DELETE requests. It will be discussed in some detail in Chapter 10.
- ``request.user_agent()`` parses the user_agent field from the client and returns the information in the form of a dictionary. It is useful to detect mobile devices. It uses "gluon/contrib/user_agent_parser.py" created by Ross Peoples. To see what it does, try to embed the following code in a view:
``
{{=BEAUTIFY(request.user_agent())}}
``:code
- ``request.global_settings`` ``request.global_settings``:inxx contains web2py system wide settings. They are set automatically and you should not change them. For example ``request.global_settings.gluon_parent`` contains the full path to the web2py folder, ``request.global_settings.is_pypy`` determines if web2py is running on PyPy.
- ``request.wsgi`` is a hook that allows you to call third party WSGI applications from inside actions
The latter includes:
- ``request.wsgi.environ``
- ``request.wsgi.start_response``
- ``request.wsgi.middleware``
their usage is discussed at the end of this Chapter.
As an example, the following call on a typical system:
``
http://127.0.0.1:8000/examples/default/status/x/y/z?p=1&q=2
``:code
results in the following ``request`` object:
``request``:inxx ``env``:inxx
----------
**variable** | **value**
Also notice the ``request.env.wsgi_*`` variables. They are specific to the wsgi
``response.status``:inxx
``response.stream``:inxx
``response.subtitle``:inxx
``response.title``:inxx
``response.toolbar``:inxx
``response.view``:inxx
``response.delimiters``:inxx
``response.js``:inxx
``response.write``:inxx
``response.include_files``:inxx
``response.include_meta``:inxx
``response.optimize_css``:inxx
``response.optimize_js``:inxx
``response._caller``:inxx
``response`` is another instance of the ``Storage`` class. It contains the following:
- ``response.body``: a ``StringIO`` object into which web2py writes the output page body. NEVER CHANGE THIS VARIABLE.
- ``response.cookies``: similar to ``request.cookies``, but while the latter contains the cookies sent from the client to the server, the former contains cookies sent by the server to the client. The session cookie is handled automatically.
- ``response.download(request, db)``: a method used to implement the controller function that allows downloading of uploaded files. ``request.download`` expects the last ``arg`` in ``request.args`` to be the encoded filename (i.e., the filename generated at upload time and stored in the upload field). It extracts the upload field name and table name as well as the original filename from the encoded filename. ``response.download`` takes two optional arguments: ``chunk_size`` sets the size in bytes for chunked streaming (defaults to 64K), and ``attachments`` determines whether the downloaded file should be treated as an attachment or not (default to ``True``). Note, ``response.download`` is specifically for downloading files associated with ``db`` upload fields. Use ``response.stream`` (see below) for other types of file downloads and streaming. Also, note that it is not necessary to use ``response.download`` to access files uploaded to the /static folder -- static files can (and generally should) be accessed directly via URL (e.g., /app/static/files/myfile.pdf).
- ``response.files``: a list of .css, .js, coffee, and .less files required by the page. They will automatically be linked in the head of the standard "layout.html" via the included "web2py_ajax.html". To include a new CSS, JS, COFFEE, or LESS file, just append it to this list. It will handle duplicates. The order is important.
- ``response.include_files()`` generates html head tags to include all ``response.files`` (used in "views/web2py_ajax.html").
- ``response.flash``: optional parameter that may be included in the views. Normally used to notify the user about something that happened.
- ``response.headers``: a ``dict`` for HTTP response headers. Web2py sets some headers by default, including "Content-Length", "Content-Type", and "X-Powered-By" (set equal to web2py). Web2py also sets the "Cache-Control", "Expires", and "Pragma" headers to prevent client-side caching, except for static file requests, for which client-side caching is enabled. The headers that web2py sets can be overwritten or removed, and new headers can be added (e.g., ``response.headers['Cache-Control'] = 'private'``). You can remove a header by removing its key from the response.headers dict, e.g.``del response.headers['Custom-Header']``, however web2py's default headers will be re-added just before returning the response. To avoid this behavior, just set the header value to None, e.g. to remove the default Content-Type header, ``response.headers['Content-Type'] = None``
- ``response.menu``: optional parameter that may be included in the views, normally used to pass a navigation menu tree to the view. It can be rendered by the MENU helper.
- ``response.meta``: a storage object (like a dict) that contains optional meta information like ``response.meta.author``, ``.description``, and/or ``.keywords``. The content of each meta variable is automatically placed in the proper ``META`` tag by the code in "views/web2py_ajax.html", which is included by default in "views/layout.html".
- ``response.include_meta()`` generates a string that includes all ``response.meta`` headers serialized (used in "views/web2py_ajax.html").
- ``response.postprocessing``: this is a list of functions, empty by default. These functions are used to filter the response object at the output of an action, before the output is rendered by the view. It can be used to implement support for other template languages.
- ``response.render(view, vars)``: a method used to call the view explicitly inside the controller. ``view`` is an optional parameter which is the name of the view file, ``vars`` is a dictionary of named values passed to the view.
- ``response.session_file``: file stream containing the session.
- ``response.session_file_name``: name of the file where the session will be saved.
- ``response.session_id``: the id of the current session. It is determined automatically. NEVER CHANGE THIS VARIABLE.
- ``response.session_id_name``: the name of the session cookie for this application. NEVER CHANGE THIS VARIABLE.
- ``response.status``: the HTTP status code integer to be passed to the response. Default is 200 (OK).
- ``response.stream(file, chunk_size, request=request, attachment=False, filename=None, headers=None)``: when a controller returns it, web2py streams the file content back to the client in blocks of size ``chunk_size``. The ``request`` parameter is required to use the chunk start in the HTTP header. ``file`` should be a file path (for backward compatibility, it can also be an open file object, but this is not recommended). As noted above, ``response.download`` should be used to retrieve files stored via an upload field. ``response.stream`` can be used in other cases, such as returning a temporary file or StringIO object created by the controller. If ``attachment`` is True, the Content-Disposition header will be set to "attachment", and if ``filename`` is also provided, it will be added to the Content-Disposition header as well (but only when ``attachment`` is True). If not already included in ``response.headers``, the following response headers will be set automatically: Content-Type, Content-Length, Cache-Control, Pragma, and Last-Modified (the latter three are set to allow browser caching of the file). To override any of these automatic header settings, simply set them in ``response.headers`` before calling ``response.stream``.
- ``response.subtitle``: optional parameter that may be included in the views. It should contain the subtitle of the page.
- ``response.title``: optional parameter that may be included in the views. It should contain the title of the page and should be rendered by the HTML title TAG in the header.
- ``response.toolbar``: a function that allows you to embed a toolbar into page for debugging purposes ``{{=response.toolbar()}}``. The toolbar displays request, response, session variables and database access time for each query.
- ``response._vars``: this variable is accessible only in a view, not in the action. It contains the value returned by the action to the view.
- ``response._caller``: this is a function that wraps all action calls. It defaults to the identity function, but it can be modified in order to catch special types of exception to do extra logging;
``
response._caller = lambda f: f()
``
- ``response.optimize_css``: if can be set to "concat,minify,inline" to concatenate, minify and inline the CSS files included by web2py.
- ``response.optimize_js``: if can be set to "concat,minify,inline" to concatenate, minify and inline the JavaScript files included by web2py.
- ``response.view``: the name of the view template that must render the page. This is set by default to:
``
"%s/%s.%s" % (request.controller, request.function, request.extension)
``:code
or, if the above file cannot be located, to
``
"generic.%s" % (request.extension)
``:code
Change the value of this variable to modify the view file associated with a particular action.
- ``response.delimiters`` defaults to ``('{{','}}')``. It allows you to change the delimiter of code embedded in views.
- ``response.xmlrpc(request, methods)``: when a controller returns it, this function exposes the methods via XML-RPC``xmlrpc``:cite . This function is deprecated since a better mechanism is available and described in Chapter 10.
- ``response.write(text)``: a method to write text into the output page body.
- ``response.js`` can contain Javascript code. This code will be executed if and only if the response is received by a web2py component as discussed in Chapter 12.
Since ``response`` is a ``gluon.storage.Storage`` object, it can be used to store other attributes that you may want to pass to the view. While there is no technical restriction, our recommendation is to store only variables that are to be rendered by all pages in the overall layout ("layout.html").
Anyway, we strongly suggest to stick to the variables listed here:
``
response.title
response.subtitle
response.flash
response.menu
response.meta.author
response.meta.description
response.meta.keywords
response.meta.*
``:code
because this will make it easier for you to replace the standard "layout.html" file that comes with web2py with another layout file, one that uses the same set of variables.
Old versions of web2py used ``response.author`` instead of ``response.meta.author`` and similar for the other meta attributes.
### ``session``
``session``:inxx ``session.connect``:inxx ``session.forget``:inxx ``session.secure``:inxx
``session`` is another instance of the ``Storage`` class. Whatever is stored into ``session`` for example:
``
session.myvariable = "hello"
``:code
can be retrieved at a later time:
``
a = session.myvariable
``:code
as long as the code is executed within the same session by the same user (provided the user has not deleted session cookies and the session has not expired). Because ``session`` is a ``Storage`` object, trying to access an attribute/key that has not been set does not raise an exception; it returns ``None`` instead.
The session object has three important methods. One is ``forget``:
``
session.forget(response)
``:code
It tells web2py not to save the session. This should be used in those controllers whose actions are called often and do not need to track user activity. ``session.forget()`` prevents the session file from being written, regardless of whether it has been modified. ``session.forget(response)`` additionally unlocks and closes the session file. You rarely need to call this method since sessions are not saved when they are not changed. However, if the page makes multiple simultaneous Ajax requests, it is a good idea for the actions called via Ajax to call ``session.forget(response)`` (assuming the session is not needed by the action). Otherwise, each Ajax action will have to wait for the previous one to complete (and unlock the session file) before proceeding, which will slow down the page loading. Notice that sessions are not locked when stored in the database.
Another method is:
``
session.secure()
``:code
which tells web2py to set the session cookie to be a secure cookie. This should be set if the app is going over https. By setting the session cookie to be secure, the server is asking the browser not to send the session cookie back to the server unless over an https connection.
The other method is ``connect``.
By default sessions are stored on the filesystem and a session cookie is used to store and retrieve the ``session.id``. Using the connect method it is possible to tell web2y to store sessions in the database or in the cookies thus eliminating need to access the filesystem for session management.
For example to **store sessions in the database**:
``
session.connect(request, response, db, masterapp=None)
``:code
where ``db`` is the name of an open database connection (as returned by the DAL). It tells web2py that you want to store the sessions in the database and not on the filesystem. ``session.connect`` must come after ``db=DAL(...)``, but before any other logic that requires session, for example, setting up ``Auth``.
To **store sessions in cookies** instead you can do:
session.connect(request,response,cookie_key='yoursecret',compression_level=None)
``:code
Here ``cookie_key`` is a symmetric encryption key.
``compression_level`` is an optional ``zlib`` encryption level.
While sessions in cookie are often recommended for scalability reason they are limited in size. Large sessions will result in broken cookies.
You can check the state of your application at any time by printing the ``request``, ``session`` and ``response`` system variables. One way to do it is to create a dedicated action:
``
def status():
return dict(request=request, session=session, response=response)
``:code
In the "generic.html" view this is done using ``{{=response.toolbar()}}``.
#### Separate sessions
If you are storing sessions on filesystems and you have lots of them, the file system access may become a bottle-neck. One solution is the following:
+If you are storing sessions on filesystem and you have lots of them, the file system access may become a bottle-neck. One solution is the following:
``
session.connect(request, response, separate=True)
``:code
By setting ``separate=True`` web2py will store sessions not in the "sessions/" folder but in subfolders of the "sessions/" folder. The subfolder will be created automatically. Sessions with the same prefix will be in the same subfolder. Again, note that the above must be called before any logic that might require the session.
### ``cache``
``cache``:inxx ``cache.ram``:inxx ``cache.disk``:inxx
``cache`` a global object also available in the web2py execution environment. It has two attributes:
- ``cache.ram``: the application cache in main memory.
- ``cache.disk``: the application cache on disk.
``cache`` is callable, this allows it to be used as a decorator for caching actions and views.
The following example caches the ``time.ctime()`` function in RAM:
``
def cache_in_ram():
import time
t = cache.ram('time', lambda: time.ctime(), time_expire=5)
return dict(time=t, link=A('click me', _href=request.url))
``:code
def cache_on_disk():
return dict(time=t, link=A('click me', _href=request.url))
``:code
The output of ``lambda: time.ctime()`` is cached on disk (using the shelve module) for 5 seconds.
Note, the second argument to ``cache.ram`` and ``cache.disk`` must be a function or callable object. If you want to cache an existing object rather than the output of a function, you can simply return it via a lambda function:
``
cache.ram('myobject', lambda: myobject, time_expire=60*60*24)
``:code
The next example caches the ``time.ctime()`` function to both RAM and disk:
``
def cache_in_ram_and_disk():
import time
t = cache.ram('time', lambda: cache.disk('time',
lambda: time.ctime(), time_expire=5),
time_expire=5)
return dict(time=t, link=A('click me', _href=request.url))
``:code
The output of ``lambda: time.ctime()`` is cached on disk (using the shelve module) and then in RAM for 5 seconds. web2py looks in RAM first and if not there it looks on disk. If it is not in RAM or on disk, ``lambda: time.ctime()`` is executed and the cache is updated. This technique is useful in a multiprocessor environment. The two times do not have to be the same.
The following example is caching in RAM the output of the controller function (but not the view):
``cache controller``:inxx
``
@cache(request.env.path_info, time_expire=5, cache_model=cache.ram)
def cache_controller_in_ram():
import time
t = time.ctime()
return dict(time=t, link=A('click me', _href=request.url))
``:code
The dictionary returned by ``cache_controller_in_ram`` is cached in RAM for 5 seconds. Note that the result of a database select cannot be cached without first being serialized. A better way is to cache the database select directly using the ``select`` method's ``cache`` argument.
The following example is caching the output of the controller function on disk (but not the view):
``
@cache(request.env.path_info, time_expire=5, cache_model=cache.disk)
def cache_controller_on_disk():
import time
t = time.ctime()
return dict(time=t, link=A('click to reload',
_href=request.url))
``:code
The dictionary returned by ``cache_controller_on_disk`` is cached on disk for 5 seconds. Remember that web2py cannot cache a dictionary that contains un-pickleable objects.
It is also possible to cache the view. The trick is to render the view in the controller function, so that the controller returns a string. This is done by returning ``response.render(d)`` where ``d`` is the dictionary we intended to pass to the view. The following example caches the output of the controller function in RAM (including the rendered view):
``cache view``:inxx
``
@cache(request.env.path_info, time_expire=5, cache_model=cache.ram)
def cache_controller_and_view():
import time
Note, ``time_expire`` is used to compare the current time with the time the requ
``
message = cache.ram('message', lambda: 'Hello', time_expire=5)
``:code
Now, suppose the following call is made 10 seconds after the above call:
``
message = cache.ram('message', lambda: 'Goodbye', time_expire=20)
``:code
Because ``time_expire`` is set to 20 seconds in the second call and only 10 seconds has elapsed since the message was first saved, the value "Hello" will be retrieved from the cache, and it will not be updated with "Goodbye". The ``time_expire`` value of 5 seconds in the first call has no impact on the second call.
Setting ``time_expire=0`` (or a negative value) forces the cached item to be refreshed (because the elapsed time since the last save will always be > 0), and setting ``time_expire=None`` forces retrieval of the cached value, regardless of the time elapsed since it was saved (if ``time_expire`` is always ``None``, the cached item will effectively never expire).
You can clear one or more cache variables with
``cache clear``:inxx
``
cache.ram.clear(regex='...')
``:code
where ``regex`` is a regular expression matching all the keys you want removed from the cache. You can also clear a single item with:
``
cache.ram(key, None)
``:code
where ``key`` is the key of the cached item.
It is also possible to define other caching mechanisms such as memcache. Memcache is available via ``gluon.contrib.memcache`` and is discussed in more details in Chapter 14.
------
Be careful when caching to remeber that caching is usually at the app-level not at the user level. If you need, for example, to cache user specific content, choose a key that includes the user id.
------
### ``URL``
``URL``:inxx
The ``URL`` function is one of the most important functions in web2py. It generates internal URL paths for the actions and the static files.
Here is an example:
``
URL('f')
``:code
is mapped into
``
/[application]/[controller]/f
``:code
Notice that the output of the ``URL`` function depends on the name of the current application, the calling controller, and other parameters. web2py supports URL mapping and reverse URL mapping. URL mapping allows you to redefine the format of external URLs. If you use the ``URL`` function to generate all the internal URLs, then additions or changes to URL mappings will prevent broken links within the web2py application.
The most common way to use redirect is to redirect to other pages in the same ap
redirect(URL('index', args=(1,2,3), vars=dict(a='b')))
``:code
In chapter 12 we discuss web2py components. They make Ajax requests to web2py actions. If the called action performs a redirect, you may want the Ajax request to follow the redirect or you may want the entire page performing the Ajax request redirecting. In this latter case you can set:
``
redirect(...,type='auto')
``:code
### ``T``, Internationalization, and Pluralization
``T``:inxx ``internationalization``:inxx
The object ``T`` is the language translator. It constitutes a single global instance of the web2py class ``gluon.language.translator``. All string constants (and only string constants) should be marked by ``T``, for example:
``
a = T("hello world")
``:code
Strings that are marked with ``T`` are identified by web2py as needing language translation and they will be translated when the code (in the model, controller, or view) is executed. If the string to be translated is not a constant but a variable, it will be added to the translation file at runtime (except on GAE) to be translated later.
The ``T`` object can also contain interpolated variables and supports multiple equivalent syntaxes:
``
a = T("hello %s", ('Tim',))
a = T("hello %(name)s", dict(name='Tim'))
a = T("hello %s") % ('Tim',)
a = T("hello %(name)s") % dict(name='Tim')
``:code
The latter syntax is recommended because it makes translation easier.
The first string is translated according to the requested language file and the ``name`` variable is replaced independently of the language.
You can concatenating translated strings and normal strings:
``
T("blah ") + name + T(" blah")
``:code
The following code is also allowed and often preferable:
``
T("blah %(name)s blah", dict(name='Tim'))
``:code
T("blah %(name)s blah" % dict(name='Tim'))
``:code
because translation would occur after substitution.
#### Determining the language
The requested language is determined by the "Accept-Language" field in the HTTP header, but this selection can be overwritten programmatically by requesting a specific file, for example:
``
T.force('it-it')
``:code
which reads the "languages/it-it.py" language file. Language files can be created and edited via the administrative interface.
You can also force a per-string language:
``
T("Hello World", language="it-it")
``:code
--------------
In the case multiple languages are requested, for example "it-it, fr-ft", web2py tries to locate "it-it.py" and "fr-fr.py" translation files. If none of the requested files is present, it tries to fall back on "it.py" and "fr.py". If these files are not present it defaults to "default.py". If this is not present either, it default to no-translation. The more general rule is that web2py tries "xx-xy-yy.py", "xx-xy.py", "xx.py", "default.py" for each of the "xx-xy-yy" accepted languages trying to find the closest match to the visitor's preferences.
-------------
You can turn off translations completely via
``
T.force(None)
``:code
Normally, string translation is evaluated lazily when the view is rendered; hence, the translator ``force`` method should not be called inside a view.
It is possible to disable lazy evaluation via
``
T.lazy = False
``:code
In this way, strings are translated immediately by the ``T`` operator based on the currently accepted or forced language.
It is also possible to disable lazy evaluation for individual strings:
``
Notice that this can result in lots of file IO and you may want to disable it:
``
T.is_writable = False
``:code
prevents T from dynamically updating language files.
#### Comments and multiple translations
It is possible that the same string appears in different contexts in the application and needs different translations based on context. In order to do this, one can add comments to the original string. The comments will not be rendered but will be used by web2py to determine the most appropriate translation. For example:
``
T("hello world ## first occurrence")
T("hello world ## second occurrence")
``:code
The text following the ``##``, including the double ``##``, are comments.
#### Pluralization engine
Since version 2.0, web2py includes a powerful pluralization system (PS). This means that when text marked for translation depends on a numeric variable, it may be translated differently based on the numeric value. For example in English we may render:
``
x book(s)
``
with
``
a book (x==0)
x books (x>0)
``
English has one singular form and one plural form. The plural form is constructed by adding a "-s" or "-es" or using an exceptional form. web2py provides a way to define pluralization rules for each languages, as well as exceptions to the default rules. In fact web2py already knows pluralization rules for many languages. It knows, for example, that Slovenian has one singular form and 3 plural forms (for x==1, x==3 or x==4 and x>4). These rules are encoded in "gluon/contrib/plural_rules/*.py" files and new files can be created. Explicit pluralizations for words are created by editing pluralization files using the administrative interface.
By default the PS is not activated. It is triggered by the ``symbol`` argument of the ``T`` function. For example:
``
T("You have %s %%{book}", symbols=10)
``:code
Now the PS is activated for the word "book" and for the number 10.
The result in English will be: "You have 10 books". Notice that "book" has been pluralized into "books".
The PS consists of 3 parts:
- placeholders ``%%{}`` to mark words in ``T``-messages
- rule to give a decision which word form to use ("rules/plural_rules/*.py")
- dictionary with word plural forms ("app/languages/plural-*.py")
The value of symbols can be a single variable, a list/tuple of variables, or a dictionary.
The placeholder ``%%{}`` consists of 3 parts:
``
%%{[<modifier>]<world>[<parameter>]},
``
where:
``
<modifier>::= ! | !! | !!!
<word> ::= any word or phrase in singular in lower case (!)
<parameter> ::= [index] | (key) | (number)
``
T("blabla %(var1)s %(wordcnt)s %%{word(wordcnt)}",
``
which produces
``
blabla tututu 20 words
``
You can replace "1" with any word you wish by this placeholder ``%%{?word?number}``.
For example
``T("%%{this} %%{is} %%{?a?%s} %%{book}", var)``
produces:
``
var output
------------------
1 this is a book
2 these are 2 books
3 these are 3 books
...
``
Inside ``%%{...}`` you can also use the following modifiers:
- ``!`` to capitalize the text (equivalent to ``string.capitalize``)
- ``!!`` to capitalize every word (equivalent to ``string.title``)
- ``!!!`` to capitalize every character (equivalent to ``string.upper``)
Notice you can use ``\\`` to escape ``!`` and ``?``.
#### Translations, pluralization, and MARKMIN
You can also use the powerful MARKMIN syntax inside translation strings by replacing
``
T("hello world")
``:code
with
python web2py.py -a yourpass -K myapp -X
You can pass the usual parameters (-i, -p, here -a prevents the window from showing up), pass whatever app in the -K parameter and append a -X. The scheduler will run alongside the webserver!
Scheduler's complete signature is:
``
Scheduler(
db,
tasks=None,
migrate=True,
worker_name=None,
group_names=None,
heartbeat=HEARTBEAT,
max_empty_runs=0,
discard_results=False,
utc_time=False
)
``:code
Let's see them in order:
- ``db`` is the database DAL instance where you want the scheduler tables be placed.
- ``tasks`` can be a dict. Must be defined if you want to call a function not by his name, i.e. ``tasks=dict(mynameddemo1=task_add)`` will let you execute function demo1 with ``scheduler.queue_task('mynameddemo1')`` instead of ``scheduler.queue_task('task_add')``. If you don't pass this parameter, function will be searched in the app environment.
- ``worker_name`` is None by default. As soon as the worker is started, a worker name is generated as hostname-uuid. If you want to specify that, be sure that it's unique.
- ``group_names`` is by default set to **[main]**. All tasks have a ``group_name`` parameter, set to **main** by default. Workers can only pick up tasks of their assigned group.
------
NB: This is useful if you have different workers instances (e.g. on different machines) and you want to assign tasks to a specific worker.
NB2: It's possible to assign a worker more groups, and they can be also all the same, as
------
``['mygroup','mygroup']``. Tasks will be distributed taking into consideration that
a worker with group_names ``['mygroup','mygroup']`` is able to process the double of the tasks
a worker with group_names ``['mygroup']`` is.
- ``heartbeat`` is by default set to 3 seconds. This parameter is the one controlling how often a scheduler will check its status on the ``scheduler_worker`` table and see if there are any **ASSIGNED** tasks to itself to process.
- ``max_empty_runs`` is 0 by default, that means that the worker will continue to process tasks as soon as they are **ASSIGNED**. If you set this to a value of, let's say, 10, a worker will die automatically if it's **ACTIVE** and no tasks are **ASSIGNED** to it for 10 loops. A loop is when a worker searches for tasks, every 3 seconds (or the set ``heartbeat``)
- ``discard_results`` is False by default. If set to True, no scheduler_run records will be created.
------
NB: scheduler_run records will be created as before for **FAILED**, **TIMEOUT** and **STOPPED** tasks's statuses.
------
The meaning of the fields in this table is obvious. The "args" and "vars"" field
``
args = [3, 4]
vars = {}
``:code
or
``
args = []
vars = {'a':3, 'b':4}
``:code
The ``scheduler_task`` table is the one where tasks are organized.
All tasks follow a lifecycle
[[scheduler tasks @///image/ce8edcc3.png center]]
Let's go with order. By default, when you send a task to the scheduler, you'll want that to be executed. It's in **QUEUED** status.
If you need it to be executed later, use the ``start_time`` parameter (default = now).
If for some reason you need to be sure that the task don't get executed after a certain point in time (maybe a request to a web service
that shuts down at 1AM, a mail that needs to be sent not after the working hours, etc...) you can set a ``stop_time`` (default = None) for it.
If your task is NOT picked up by a worker before stop_time, it will be set as **EXPIRED**.
Tasks with no stop_time set or picked up **BEFORE** stop_time are **ASSIGNED** to a worker. When a workers picks up them, they become **RUNNING**.
**RUNNING** tasks may end up:
- **TIMEOUT** when more than n seconds passed with ``timeout`` parameter (default = 60 seconds)
- **FAILED** when an exception is detected
- **COMPLETED** when all went ok
Additionally, you can control how many times a task should be repeated (i.e. you need to aggregate some data at specified intervals). To do so, set the ``repeats``
parameter (default = 1 time only, 0 = unlimited). You can influence how many seconds should pass between executions with the ``period`` parameter (default = 60 seconds).
------
NB: the time is not calculated between the END of the first round and the START of the next, but from the START time of the first round to the START time of the next cycle)
------
Another nice addition, you can set how many times the function can raise an exception (i.e. requesting data from a slow web service) and be queued again instead of stopping in **FAILED** status with the parameter ``retry_failed`` (default = 0, -1 = unlimited).