wq Configuration Object
The wq Configuration object describes the global settings and navigation structure of a wq-powered website/app and its corresponding REST API. The configuration object is typically generated by the Python service (wq.db) and provided to the JavaScript client (wq.app) in order to set common expectations on the layout of the application.
The basic format of the wq Configuration Object is:
{
// Global Settings
"site_title": "[Project Name]",
"logo": "/path/to/logo.png",
"material": {
"theme": {
"primary": "#7500ae",
"secondary": "#0088bd"
}
},
"map": {
"bounds": [[-180, -70], [180, 70]]
},
// Navigation Structure
"router": {
"base_url": ""
},
"store": {
"service": "",
"defaults": {
"format": "json"
}
},
"pages": {
"[model_name]": {
"list": true,
"url": "[model_name]s",
"form": [...]
},
"[simple_page]": {
"url": "[simple_page]"
}
}
}
In the default project layout, wq.db exports the configuration object to the JavaScript module app/js/data/config.js
. This module is imported by app/js/[myproject].js
which passes it along to wq.init()
.
Global Settings
The following configuration options can be set by updating the corresponding WQ_CONFIG
key (which is defined in db/[myproject]/settings/base.py
in the default layout.)
Option | Description |
---|---|
logo |
Path to image to to display in site Header |
site_title |
Title to display in Header and browser title bar. (Defaults to settings.PROJECT_NAME ) |
backgroundSync |
Whether to sync form submissions in the background (true , default) or wait until the form is submitted before navigating (false ). Can be overridden on a per-page basis. |
debug |
Enable debug mode in @wq/router and other modules. (Defaults to settings.DEBUG ) |
material |
Configuration for @wq/material including site theme |
map |
Configuration for @wq/map including default geographical bounds |
[plugin name] |
Configuration options for other registered plugins. |
Navigation Structure
In addition to the global settings, the wq configuration object also describes the URL structure of the application. This is done through the following sections:
Option | Description |
---|---|
router |
Configuration for @wq/router, including the base url of the application (if not at website root URL.) |
store |
Configuration for @wq/store, including the service URL for the REST API (if not also at website root URL.) |
pages |
Page-specific configuration for all registered models and routes. |
These three sections of the wq configuration object are generated by wq.db automatically, and should not be specified manually except in advanced use cases. Page-specific configuration can be supplied when registering models and pages with the router, as described below.
Page Registration
The primary configuration section in the wq configuration object is pages
, which contains detailed information about the URL structure of the application. For example, the pages
configuration is used by @wq/app to map URL requests to page templates to render, and also to provide hints to @wq/store and @wq/model as to the structure of the REST API containing the actual data.
As its name implies, the pages
section summarizes all of the available “pages” in the application. The section is a mapping of page “names” to simple configuration objects describing each respective page. Pages fall into two categories depending on whether or not they are backed by a database table (i.e. ORM model in a Django application).
- Pages backed by a database table are indicated with
"list": true
. The name of these pages is the name of the underlying database model. Marking a page as"list": true
implies the presence of list, detail, and edit views for a database entity - all with corresponding URLs defined relative to theurl
property defined for the page. List pages are configured in wq.db withrest.router.register_model()
.
# myapp/rest.py
from wq.db import rest
from .models import MyModel
rest.router.register_model(
MyModel,
fields="__all__",
cache="all", # wq configuration
my_custom_flag=True, # Custom configuration
)
- Pages without a corresponding model do not have the
list
property set, and have only a single URL which is specified by theurl
property. By convention, the name of these pages is usually the same as the URL. These pages are configured in wq.db withrest.router.add_page()
.
# myapp/rest.py
from wq.db import rest
rest.router.add_page("about", {"url": "about"})
The full listing of page configuration options is described below.
General Options
Name | Usage |
---|---|
list |
Boolean; set automatically by register_model . Sets whether or this is a list page or a simple page. If true, a minimum of three routes will be generated: [model_name]_list , [model_name]_detail , and [model_name]_edit . If false or unset, it there will be a single route with the same name as the page. |
url |
The URL of the page. For simple pages, this is conventionally the same as the name of the page. For list views, it is typically the verbose plural name of the model (without spaces). The list view will be rendered at [url]/ , while detail and edit views will be constructed from this value as [url]/[item_id] and [url]/[item_id]/edit , respectively. |
name |
The name of the page, usually based on the model class name. Must be unique in the configuration; if two models have identical class names this property can be overridden to avoid conflicts. |
show_in_index |
Whether to link to this route in the default Index / NavMenu. |
verbose_name |
Singular friendly name for page / model to show in navigation and Index / NavMenu. |
order |
Relative ranking within Index / NavMenu |
section |
Name of subheading to group related links in Index / NavMenu |
icon |
Registered [icon] to use for link in Index / NavMenu |
description |
Description text to use for link in Index / NavMenu |
map |
Map configuration for the @wq/map plugin. See the @wq/map documentation for more details. |
Additional Options for List Pages
As noted above, “list” pages are typically backed by ORM models which in turn correspond to database tables.
Name | Usage |
---|---|
verbose_name_plural |
Plural friendly name of the model (default verbose_name + "s" ) to show in navigation. Set automatically by register_model . |
form |
A representation of the model schema, in the form of an array of field definitions (see below). Set automatically by register_model . |
per_page |
Default number of results that will be returned per page in paginated lists. This can be overridden by the limit url argument. (Note: if you are setting per_page to a high number like 1000, cache="all" might be what you really need.) |
lookup |
Name of identifier column to use when looking up objects for detail views, and to use as the id attribute in serialized JSON data. Defaults to the model primary key. |
cache |
Controls whether some or all of the model data is stored for offline use (see below) |
background_sync |
Controls whether record submissions are synced in the background (while navigating), or foreground (before navigating). Defaults to the global setting. |
postsave |
Controls which page is displayed after a record is submitted (see below). |
postdelete |
Controls which page is displayed after a record is deleted (see below). |
cache
Configuration
The cache
option configures which records from the model are cached for offline use (by @wq/model). Five caching modes are available:
cache= |
Description |
---|---|
"first_page" |
(Default) The first 50 (or per_page ) items will be stored offline; subsequent records can be accessed through pagination. |
"all" |
The entire dataset will be stored for offline use. (Useful for domain values / foreign keys) |
"filter" |
Specify a custom filter for the offline cache. Requests for additional data are not cached and will always hit the server. |
"autoupdate" |
An initial set of filtered records will be cached. Requests for additional data are cached and will only hit the server if no local results are found. |
"none" |
None of the dataset will be stored offline; every list and detail view will require a network request. |
Note that the "filter"
and "autoupdate"
options assume that a cache_filter
Python function will be registered with wq.db’s router to do the actual filtering:
# myapp/rest.py
from wq.db import rest
from .models import MyModel
def user_filter(qs, request):
if request.user.is_authenticated:
return qs.filter(user=request.user)
else:
return qs.none()
rest.router.register_model(
MyModel,
fields="__all__",
cache_filter=user_filter,
# Implied:
# cache="filter"
)
See the router registration documentation for more information about the cache_filter
option and how it is interpreted by wq.db.
postsave
and postdelete
By default, the next step after saving a record is to return to the list view / outbox (for background synced records) or to return to the detail view (for foreground synced records). This behavior can be customized with the postsave
configuration option. postsave
can take two forms: a template name, or a URL with Mustache-style placeholders.
Suppose there are two models, Site
and Observation
. The postsave
for Observation
might be any of the following:
postsave= |
Description |
---|---|
"observation_list" |
Return to the list of observations and outbox (default for background synced records). |
"observation_detail" |
Return to the detail page for the current observation (default for foreground synced records). |
"site_detail" |
Return to the detail page for the site referenced by the current observation’s foreign key. |
"sites//observations" |
Return to the list of observations filtered by the referenced site. |
"observations/new?site_id=" |
Start a new observation for the same site. |
postdelete
has similar semantics as postsave
, but does not support variable placeholders or returning to detail views, since the object has already been deleted by the time the postdelete
action is executed. The default for postdelete
is always to return to the list view.
Form Fields
The configuration object for each list page includes an XLSForm-inspired representation of the schema for the model. This information is used by the <AutoForm/>
component to automatically generate the appropriate form inputs for editing a page. Certain properties are also used by @wq/app to handle choice lookups and relationships between models.
The form
attribute is an array of wq field definitions, which are JSON objects with any of the following attributes. Where applicable, the corresponding concepts in the XLSForm Question and Django Field definitions are listed.
wq Field | XLSForm Question | Django Field | Usage |
---|---|---|---|
name |
name |
name |
Internal name for the field |
type |
type |
(derived from class) | XLSForm type (e.g. int , string , select1 , binary . The group and repeat types are used together with the children attribute (below). |
label |
label |
verbose_name |
User-friendly label for the field |
hint |
hint |
help_text |
Longer description of the field or how the user should populate it |
bind.required |
bind.required |
(opposite of null ) |
Whether the field is required or optional. The format bind: {required: true} is for compatibility with the XLSForm syntax. |
wq:length |
n/a | max_length |
Maximum allowed length for string fields |
choices |
choices |
(derived from choices ) |
An array of valid choices for enum-style fields on the model in the format {'name': '(user-friendly name)', 'value': '(internal value)'} . This information is surfaced in the [model_name]_edit [rendering context] as [field_name]_choices , with a selected flag set on the currently selected option when editing an existing record. |
wq:ForeignKey |
n/a | (derived from model name) | This is used by @wq/app to automatically retreive a list of choices from the referenced model when rendering a form. The list of potential parent records is surfaced in the [model_name]_edit [rendering context] as [field_name]_list , with id and label attributes for each record in the parent model, and a selected flag set on the currently selected parent when editing an existing record. Note that the referenced model should also be registered in the configuration object for all of this this to work. |
filter |
n/a | n/a | An optional filter to apply to the list of potential parent records referenced by a ForeignKey. This is passed to model.filter() on the parent model. |
children |
children |
(c.f. related_name and inline formsets) |
This defines a nested list of fields that represent sub-observations or a “tall schema” structure. The structure is an array of fields in the same format as the top-level form array. If the field type is group , the nested fields will be repeated once in the form. If the field type is repeat , the fields can be repeated multiple times, with a button to add additional sub-observations. The nested observations would normally be stored in an auxilary table with a ForeignKey pointing to this one. The auxilary table does not need be registered with the configuration separately. |
initial |
n/a | n/a | Configuration for determining how to generate nested repeat observations when creating a “new” record. By default, no nested records will be displayed at first, but a button will be provided to add one or more nested records. To simulate Django’s default of 3 nested inline forms, set initial to “3”. To leverage an EAV structure (where each nested record corresponds to a row in a separate “Attribute” or “type” table), set initial to an object specifying the name of the type model and a filter (if any) to use with model.filter() when generating the list, e.g. {"type_model": "attribute", "filter": {"active": true} . |