THIS PAGE IS PARTIALLY OUT OF DATE
Sahana Eden Framework
Table of Contents
We have built an S3 framework as a higher level of abstraction on top of Web2Py.
This should be used by modules where possible, but if more power is needed then drop down a level or two (to base Web2Py or to raw Python).
Web2py Extensions
We extend some of the web2py framework classes in modules/s3
.
We replace web2py's Crud with a REST Controller.
Patches to the framework should be done in modules/s3
.
If they're generic enough then they can be suggested to Massimo Di Pierro for moving upstream to web2py.
Mandatory
Each Controller should start like this:
module = request.controller resourcename = request.function if not deployment_settings.has_module(module): raise HTTP(404, body="Module disabled: %s" % module)
Menus
Normal menus are defined in modules/s3menus.py
Custom menus are defined in modules/templates/<templatename>/menus.py
Conflict Detection
Sahana is a multi-user system so there is a potential for multiple users to be editing the same record at once.
We use Web2Py's FORM to handle this for us.
Add the timestamp
reusable field to each table which needs protecting (in modules/s3db/module.py
):
Synchronization
All tables which are user-editable need to be protected for conflict resolution & synchronization using the predefined reusable fields timestamp
& uuidstamp
- usually added via s3.metafields
.
Redirection
Remember that the interactive HTML interface is just one way to access the system. Non-interactive interfaces like Synchronization are also common, as are background interactions like AJAX.
Therefore be very wary of using Redirection, which should never be done in Validators, Onaccept or Onvalidation routines & should be done within REST Controllers only with a check:
def my_resource(): """ REST Controller for my_resource """ def prep(r): if r.interactive: redirect ok ...
Optional
Other reusable fields are available to add Foreign Key links to key tables, such as:
location_id()
to add a GIS location for Mappingperson_id()
to add a Personorganisation_id()
to add a link to an Organisation
List output are made more functional by this .represent 'widget':
def shn_list_item(table, resource, action, display="table.name", extra=None): if extra: items = DIV(TR(TD(A(eval(display), _href=t2.action(resource, [action, table.id]))), TD(eval(extra)))) else: items = DIV(A(eval(display), _href=t2.action(resource, [action, table.id]))) return DIV(*items)
This is called in models/01_crud.py
in the REST Controller:
db.pr_person.represent = lambda table:shn_list_item(table, resource="person", action="display", display="table.full_name")
Form labels can be set in a translatable manner using:
db.table.field.label = T("label")
Form field can be made to use a TEXTAREA by marking the field as being type 'text':
Field("field", "text"),
Form field can be made to use a SELECT dropdown by setting the field as a lookup to another table...linked to the 'id' field to allow Database Synchronization, but displaying a more user-friendly field (such as 'name'):
Field("field", db.othertable), db.table.field.requires = IS_NULL_OR(IS_ONE_OF(db, "othertable.id", "othertable.name"))
NOTE: The IS_IN_DB validator is not deletion status sensitive, i.e. even deleted entries in the key table can be referenced. To exclude entries that are flagged as 'deleted', use the IS_ONE_OF validator.
Form field being required can be marked using:
db.table.field.comment = SPAN("*", _class="req")
Help for a form field can be set using:
DIV(DIV(_class="tooltip", _title=T("Help Title|This is what this <a href='http://wikipedia.org/Field' target=_blank>field</a> is for.")))
Different Flash styles can be set via:
session.error = T("Unsupported format!") redirect(URL(f=resource))
or (in a Multiple Table form.accepts):
response.error = T("Form invalid!")
Supported styles are:
- .warning
- .error
- .information
- .confirmation (Default response.flash is styled like this)
Global variables
Use session for per-user persistent variables: session.s3.myvariable
Use response for one-off variables which are visible in views without explicit passing: response.s3.myvariable
Settings
deployment_settings are defined in models/000_config.py
.
Some settings are in database tables whose defaults are set in modules/templates
run by models/zzz_1st_run.py
.
Upon 1st run of the system, these settings are loaded into the database from where they can subsequently be edited to configure the running instance.
If these settings are required by all Views then can patch the session to see them in models/00_utils.py
's s3_sessions()
.
e.g. session.s3.debug is made available to the default layout.html
this way.
If you wish to clean out all settings to import afresh, then close down Web2Py & delete all files from /databases
)
jQuery
jQuery scripts are located in multiple places to provide for ease of maintenance & for performance gains:
- If there is no need for server-side processing our scripts should be added to
/static/scripts/S3/S3.js
- This gets automatically compiled into
S3.min.js
via: DeveloperGuidelinesReleaseProcess
- This gets automatically compiled into
- If there is a need for server-side pre-processing, they should be added to
/views/s3_include_debug.html
- Also needs manually copying to
/views/s3_include_min.html
- Also needs manually copying to
- Scripts from base Web2Py or T2 are stored in the appropriate areas of
/static/scripts
or/views/s3_include_*.html
Specific widgets we use:
- dataTables plugin to enhance list output with clickable columns, pagination & search box
- autocomplete plugin for not having dropdowns get too huge (e.g.
/views/msg/sms_outbox_create.html
) - Popup for Form reference data
- Subtypes in Select dropdowns
- Deletable List (old)