Version 3 (modified by Dominic König, 10 years ago) ( diff )


S3Profile: Profile Pages

A 'profile' page is designed as a way of representing a single record, along with various related resources (typically Components).

It consists of a Header, along with a number of different Widgets:

  • Map
    • displays component resources, can be made fullscreen, layers update as new records are added
  • DataList ('cards')
    • can add new records and the list will refresh accordingly
  • DataTable
    • can add new records and the list will refresh accordingly
  • Form
  • Report
  • Comments (to come...)

The profile page is accessed by calling the 'profile' method of the record:


If there are no Widgets defined, then the profile method falls back to the simpler 'read' view for the resource (which can still include Components on Tabs if these are defined in the rheader)


The profile pages are configured by configuring a few profile_* variables and storing these against the tablename with the usual s3db.configure(tablename, key=value), e.g.:

               profile_title = title,
               profile_header = DIV(H2(title),
               profile_cols = 4
               profile_widgets = [alerts_widget,
               profile_layers = [layer],
  • profile_title is a simple string to be used as the response.title of the page (which will show in the browser title)
    • if not defined, this falls-back to or T("Profile Page")
  • profile_header is HTML prepared using Web2Py's HTML helpers (gluon/ It can be simple (as it is here) or can be as complex as you wish.
    • if not defined, this falls-back to H2(title, _class="profile_header")
  • profile_cols is an integer showing how many columns the widgets are spread across
    • if not defined, this falls-back to 2
  • profile_widgets are the Widgets (see below)
  • profile_layers are additional layers to display on the map, defined like:
    layer = dict(name = record_name,
                 id = "profile-header-%s-%s" % (tablename, record_id),
                 active = True,
                 tablename = tablename,
                 url = "/%s/event/incident.geojson?" % \
                            (r.application, record_id),
                 marker = marker,

Widget configuration

Here is an example configuration for a map widget:

map_widget = dict(label = "Map",
                  type = "map",
                  context = "incident",
                  icon = "icon-map",
                  height = 600,
                  width = 200,
                  colspan = 1,
  • label is used as a display title of the widget
  • type identifies this as a Map widget
  • context is used as a filter for the map layers. See S3/Context
  • icon is used as an icon for the widget
  • height and width are used to size the map viewport
  • colspan says how many columns are taken up by the widget

Here is an example configuration for a datalist widget:

alerts_widget = dict(label = "Alerts",
                     label_create = "Create Alert",
                     type = "datalist",
                     tablename = "cms_post",
                     context = "incident",
                     # Only show Active Alerts
                     filter = S3FieldSelector("expired") == False,
                     icon = "icon-alert",
                     colspan = 1,
                     #list_layout = s3db.cms_post_list_layout,

Most of the options are the same as for the map widget, extra ones include:

  • label_create is used as a title for the popup with the create form
  • tablename is used to show which resource is used for this datalist
  • filter is used as a filter for the resource (usually an additional filter on top of the Context, although it can replace the context if-desired)
  • list_layout can define an alternate card renderer (falls-back to the default s3db.get_config(tablename, "list_layout"))

NB You should ensure that your create forms establish the context link automatically. This is usually done by checking for a context filter, and then setting table.field.default, e.g.:

incident_id = get_vars.get("~.(incident)", None)
if incident_id:
    field = table.incident_id
    field.default = incident_id
    field.readable = field.writable = False

If the link is via a link table then you need to add the link onaccept, e.g.:

incident_id = r.get_vars.get("~.(incident)", None)
if incident_id:
    # Coming from Profile page
    # Add link onaccept
    def create_onaccept(form):

                   create_onaccept = create_onaccept, 


The core functionality is in modules/s3/ although this is a very small file as most of the work is in the surrounding framework of S3REST and in the Widgets.

The hook to link the 'profile' method to this class is in s3_rest_controller() in models/

set_handler("profile", s3base.S3Profile)

The default view template is in views/_profile.html but this can be over-ridden for a sepcific template as private/templates/mytemplate/views/_profile.html

Widget code


S3Profile._datalist() (modules/s3/ instantiates an S3DataList() (modules/s3/ using resource.datalist() and datalist.html().
Client-side JavaScript is in static/scripts/S3/s3.dataTables.js.


S3Profile._datatable() (modules/s3/ instantiates an S3DataTable() (modules/s3/ using resource.datatable() and datatable.html().
Client-side JavaScript is in static/scripts/S3/s3.dataLists.js.


S3Profile._map() (modules/s3/ instantiates an S3Map() (modules/s3/ with a layer for each widget which is a datalist, datatable or report (unless that widget is configured show_on_map=False).
Client-side JavaScript is in static/scripts/S3/s3.gis.js.


S3Profile._form() (modules/s3/ instantiates an S3SQLForm() (modules/s3/
If the form has Inline components, then the client-side JavaScript for this is in static/scripts/S3/s3.inline_component.js.


S3Profile._report() (modules/s3/ instantiates an S3Report() (modules/s3/ using report.widget().
Client-side JavaScript is in static/scripts/S3/s3.jquery.ui.pivottable.js.

Future Extensions

  • Allow component resources to be filtered
  • Comments widget
  • Use widget() function of target method handler instead of internal widgets

See Also

Note: See TracWiki for help on using the wiki.