gws.base.model¶
Data models.
A data model, or simply model, is an object that deals with features from external sources, like database tables, shape files, GML responses, etc. The job of a model is to read features from the source and convert them to a form suitable for the client. An editable model can also accept features back from the client, parse and validate them, and store them in the source.
This package provides the base classes for models and their components. Concrete
models (e.g. postgres, qgis, geojson) live in gws.base.database and
in plugins, concrete fields, values, validators and widgets live in the
gws.plugin.model_field, gws.plugin.model_value, gws.plugin.model_validator
and gws.plugin.model_widget packages.
Submodules¶
core- the base model class (Object) with the model configuration protocol, props and the conversion between features and props.default_model- thedefaultmodel, used when no model is configured. It copies attributes between records, features and props as they are.manager- the model manager (root.app.modelMgr), which looks up models by uid or by the objects they belong to, and lists editable models. Editable models are collected from the project layers, the project and the application, together with their related models the user can read.field- the base model field class, with flags, values, validators, widget and validation.scalar_field- the base class for fields that map to one source attribute (column).related_field- the base class for fields that link features of other models, with the relationship description and database helpers.value- the base class for value objects.validator- the base class for validator objects.widget- the base class for client widgets.util- helpers to create model contexts and to iterate over related features.
Features¶
A feature is a collection of named attributes. One of these attributes can
act as a unique ID (uid), and another one can be the feature
geometry (shape). uid is required for editable models, shape is always optional.
There are three kinds of objects that represent features:
The feature object (gws.Feature) is the internal representation of a
feature. It provides storage for attributes and convenience methods to extract
or mutate them. A feature also contains a dict of views, which are chunks of HTML,
rendered by templates and used to represent a feature in the client.
A record object (gws.FeatureRecord) is a data object that only contains a
dict of attributes and, optionally, some metadata properties, depending on the
source. For example, GML feature records usually contain the layer name. It
represents raw source data.
A props object (gws.FeatureProps) contains the data necessary to display a feature
in the client. When viewing features, the client only needs their uid, shape and
views. In the edit context, the props object also contains a dict of attributes.
View props (feature_to_view_props) only keep the uid and geometry attributes, under
the names uid and geometry.
Operations¶
Models are used to perform several abstract operations (gws.ModelOperation):
read- the client provides a search query (gws.SearchQuery) and expects a list of matching propscreate- the client sends feature props and wants to create new features in the sourceupdate- the client sends feature props and wants to update existing featuresdelete- the client sends feature props and wants respective features to be deleted
Before create, the client can also request a new empty feature to be initialized
(init_feature) and sent back.
Fields¶
Most models contain a list of field objects (gws.ModelField). A field
deals with a subset of feature data, converts it between representations
and validates it.
When a model performs an operation, it delegates it to all its fields in turn.
Fields are either configured explicitly (fields), or created automatically from the
source columns (withAutoFields, or when no fields are configured), see
core.Object.configure_auto_fields.
There are two kinds of fields: scalar fields represent one attribute (column) in the source itself, and related fields represent features from other models, linked to the current model.
Values¶
A field can have value objects (gws.ModelValue) attached to it.
Value objects provide a compute method. When a model performs an operation,
and a field has a value object configured for this operation, its compute method
is called, and the returned value is used as the field value.
Validators¶
A field can also have validator objects (gws.ModelValidator) attached. On
create and update, validate_feature runs the validators of all fields
and collects a gws.ModelValidationError for each field that fails. Each field always has
a notEmpty and a format validator: an empty value is an error only for required fields,
and further validators are not run for an empty value. The notEmpty validator runs first,
then format, then the other validators configured for the operation; validation of a field
stops at the first failure. The error message of a validator defaults to validationError_<type>.
Widgets¶
A field can have a widget object (gws.ModelWidget), which describes how the client
displays and edits the field value. Without a configured widget, related fields of type
feature get a featureSelect widget, and fields of type featurelist get a
featureList widget.
Permissions¶
To perform a model operation, the user must have the matching permission (gws.Access)
on the model: read to find features, create to initialize and create features,
write to update and delete to delete them. Database models raise gws.ForbiddenError
when the permission is missing.
Each field can also have permissions, interpreted as follows:
read- the content of the field can be read from the source and sent to the clientwrite- user input for this field can be written to the source
It is not an error to read or write a field without permission. The attempt is silently ignored.
If a field has attached value objects, these are applied regardless of field permissions.
Data flow¶
Database models (gws.base.database.model) move data between three representations:
the record (feature.record.attributes, raw source values), the feature (feature.attributes,
python values) and the props (feature.props.attributes, client values).
Scalar fields convert between them with raw_to_python, python_to_raw,
prop_to_python and python_to_prop.
Reading:
find_features(search, mc)
for each field: before_select(mc)
add columns and conditions to mc.dbSelect
run the select, create a feature with a record for each row
for each field: after_select(features, mc)
from_record: record -> feature
Sending to the client:
feature_to_props(feature, mc)
for each field: to_props(feature, mc)
feature -> props, skipped if the user cannot read the field
Writing:
feature_from_props(props, mc)
for each field: from_props(feature, mc)
props -> feature
create_feature(feature, mc) / update_feature(feature, mc)
for each field: before_create / before_update
to_record: feature -> record, skipped for auto and virtual fields
insert or update the row from the record
for each field: after_create / after_update
When a scalar field reads a value (from_record, to_record), it uses the first value
object configured for the current operation. A value object that is not marked as default
always provides the value. Otherwise the value is taken from the source, if the user
has access to the field, and the default value object is used only if the source has no value.
Context¶
All model operations require a context data object (gws.ModelContext), usually called mc. This object contains:
the operation (
read,updateetc.)the user performing the operation
the current project
the depth of related features to load (
relDepth,maxDepth)other properties, mostly database related
Models provide methods to perform operations, while fields contain callback methods invoked by the model.
For example, here is how a database model implements the update operation:
class Model
def update_feature (feature, mc)
check if mc.user is allowed to write to this model
attach an empty record to feature
open a transaction in the source
for each field in this model
invoke the "before_update" callback
to transfer data from feature.attributes to feature.record
write changes to the source, using feature.uid as a key and feature.record as data
(e.g. UPDATE source SET ...record... WHERE id=feature.uid)
for each field in this model
invoke the "after_update" callback
to synchronize updated data, e.g. update a linked model
commit the transaction
Examples¶
An editable database model with a value and a validator:
models+ {
type "postgres"
tableName "edit.poi"
isEditable true
permissions.edit "allow all"
fields+ { name "id" type "integer" isPrimaryKey true permissions.edit "deny all" }
fields+ { name "name" type "text" isRequired true widget { type "input" } }
fields+ {
name "updated"
type "datetime"
values+ { type "currentTimestamp" forRead false }
}
fields+ { name "geom" type "geometry" }
}
Reading features of a model in Python:
mc = gws.ModelContext(op=gws.ModelOperation.read, target=gws.ModelReadTarget.map, user=user)
features = model.find_features(gws.SearchQuery(uids=['1', '2']), mc)
props = [model.feature_to_props(f, mc) for f in features]
Source code: gws.base.model