Coverage for gws-app/gws/base/model/__init__.py: 100%

3 statements  

« prev     ^ index     » next       coverage.py v7.16.2, created at 2026-10-05 13:35 +0200

1"""Data models. 

2 

3A data model, or simply model, is an object that deals with features from 

4external sources, like database tables, shape files, GML responses, etc. The 

5job of a model is to read features from the source and convert them to a form 

6suitable for the client. An editable model can also accept features back from 

7the client, parse and validate them, and store them in the source. 

8 

9This package provides the base classes for models and their components. Concrete 

10models (e.g. ``postgres``, ``qgis``, ``geojson``) live in ``gws.base.database`` and 

11in plugins, concrete fields, values, validators and widgets live in the 

12``gws.plugin.model_field``, ``gws.plugin.model_value``, ``gws.plugin.model_validator`` 

13and ``gws.plugin.model_widget`` packages. 

14 

15Submodules 

16---------- 

17 

18- ``core`` - the base model class (`Object`) with the model configuration protocol, 

19 props and the conversion between features and props. 

20- ``default_model`` - the ``default`` model, used when no model is configured. It copies 

21 attributes between records, features and props as they are. 

22- ``manager`` - the model manager (``root.app.modelMgr``), which looks up models 

23 by uid or by the objects they belong to, and lists editable models. Editable models 

24 are collected from the project layers, the project and the application, together with 

25 their related models the user can read. 

26- ``field`` - the base model field class, with flags, values, validators, widget and validation. 

27- ``scalar_field`` - the base class for fields that map to one source attribute (column). 

28- ``related_field`` - the base class for fields that link features of other models, 

29 with the relationship description and database helpers. 

30- ``value`` - the base class for value objects. 

31- ``validator`` - the base class for validator objects. 

32- ``widget`` - the base class for client widgets. 

33- ``util`` - helpers to create model contexts and to iterate over related features. 

34 

35Features 

36-------- 

37 

38A feature is a collection of named attributes. One of these attributes can 

39act as a unique ID (``uid``), and another one can be the feature 

40geometry (``shape``). ``uid`` is required for editable models, ``shape`` is always optional. 

41 

42There are three kinds of objects that represent features: 

43 

44The feature object (`gws.Feature`) is the internal representation of a 

45feature. It provides storage for attributes and convenience methods to extract 

46or mutate them. A feature also contains a dict of ``views``, which are chunks of HTML, 

47rendered by templates and used to represent a feature in the client. 

48 

49A record object (`gws.FeatureRecord`) is a data object that only contains a 

50dict of attributes and, optionally, some metadata properties, depending on the 

51source. For example, GML feature records usually contain the layer name. It 

52represents raw source data. 

53 

54A props object (`gws.FeatureProps`) contains the data necessary to display a feature 

55in the client. When viewing features, the client only needs their ``uid``, ``shape`` and 

56``views``. In the edit context, the props object also contains a dict of attributes. 

57View props (``feature_to_view_props``) only keep the uid and geometry attributes, under 

58the names ``uid`` and ``geometry``. 

59 

60Operations 

61---------- 

62 

63Models are used to perform several abstract operations (`gws.ModelOperation`): 

64 

65- ``read`` - the client provides a search query (`gws.SearchQuery`) and expects a list of matching props 

66- ``create`` - the client sends feature props and wants to create new features in the source 

67- ``update`` - the client sends feature props and wants to update existing features 

68- ``delete`` - the client sends feature props and wants respective features to be deleted 

69 

70Before ``create``, the client can also request a new empty feature to be initialized 

71(``init_feature``) and sent back. 

72 

73Fields 

74------ 

75 

76Most models contain a list of field objects (`gws.ModelField`). A field 

77deals with a subset of feature data, converts it between representations 

78and validates it. 

79 

80When a model performs an operation, it delegates it to all its fields in turn. 

81 

82Fields are either configured explicitly (``fields``), or created automatically from the 

83source columns (``withAutoFields``, or when no fields are configured), see 

84`core.Object.configure_auto_fields`. 

85 

86There are two kinds of fields: scalar fields represent one attribute (column) 

87in the source itself, and related fields represent features from other models, 

88linked to the current model. 

89 

90Values 

91------ 

92 

93A field can have value objects (`gws.ModelValue`) attached to it. 

94Value objects provide a ``compute`` method. When a model performs an operation, 

95and a field has a value object configured for this operation, its ``compute`` method 

96is called, and the returned value is used as the field value. 

97 

98Validators 

99---------- 

100 

101A field can also have validator objects (`gws.ModelValidator`) attached. On 

102``create`` and ``update``, ``validate_feature`` runs the validators of all fields 

103and collects a `gws.ModelValidationError` for each field that fails. Each field always has 

104a ``notEmpty`` and a ``format`` validator: an empty value is an error only for required fields, 

105and further validators are not run for an empty value. The ``notEmpty`` validator runs first, 

106then ``format``, then the other validators configured for the operation; validation of a field 

107stops at the first failure. The error message of a validator defaults to ``validationError_<type>``. 

108 

109Widgets 

110------- 

111 

112A field can have a widget object (`gws.ModelWidget`), which describes how the client 

113displays and edits the field value. Without a configured widget, related fields of type 

114``feature`` get a ``featureSelect`` widget, and fields of type ``featurelist`` get a 

115``featureList`` widget. 

116 

117Permissions 

118----------- 

119 

120To perform a model operation, the user must have the matching permission (`gws.Access`) 

121on the model: ``read`` to find features, ``create`` to initialize and create features, 

122``write`` to update and ``delete`` to delete them. Database models raise `gws.ForbiddenError` 

123when the permission is missing. 

124 

125Each field can also have permissions, interpreted as follows: 

126 

127- ``read`` - the content of the field can be read from the source and sent to the client 

128- ``write`` - user input for this field can be written to the source 

129 

130It is *not* an error to read or write a field without permission. 

131The attempt is silently ignored. 

132 

133If a field has attached value objects, these are applied regardless of field permissions. 

134 

135Data flow 

136--------- 

137 

138Database models (`gws.base.database.model`) move data between three representations: 

139the record (``feature.record.attributes``, raw source values), the feature (``feature.attributes``, 

140python values) and the props (``feature.props.attributes``, client values). 

141Scalar fields convert between them with ``raw_to_python``, ``python_to_raw``, 

142``prop_to_python`` and ``python_to_prop``. 

143 

144Reading:: 

145 

146 find_features(search, mc) 

147 for each field: before_select(mc) 

148 add columns and conditions to mc.dbSelect 

149 run the select, create a feature with a record for each row 

150 for each field: after_select(features, mc) 

151 from_record: record -> feature 

152 

153Sending to the client:: 

154 

155 feature_to_props(feature, mc) 

156 for each field: to_props(feature, mc) 

157 feature -> props, skipped if the user cannot read the field 

158 

159Writing:: 

160 

161 feature_from_props(props, mc) 

162 for each field: from_props(feature, mc) 

163 props -> feature 

164 

165 create_feature(feature, mc) / update_feature(feature, mc) 

166 for each field: before_create / before_update 

167 to_record: feature -> record, skipped for auto and virtual fields 

168 insert or update the row from the record 

169 for each field: after_create / after_update 

170 

171When a scalar field reads a value (``from_record``, ``to_record``), it uses the first value 

172object configured for the current operation. A value object that is not marked as default 

173always provides the value. Otherwise the value is taken from the source, if the user 

174has access to the field, and the default value object is used only if the source has no value. 

175 

176Context 

177------- 

178 

179All model operations require a context data object (`gws.ModelContext`), usually called ``mc``. This object contains: 

180 

181- the operation (``read``, ``update`` etc.) 

182- the user performing the operation 

183- the current project 

184- the depth of related features to load (``relDepth``, ``maxDepth``) 

185- other properties, mostly database related 

186 

187Models provide methods to perform operations, while fields contain callback methods invoked by the model. 

188 

189For example, here is how a database model implements the ``update`` operation:: 

190 

191 class Model 

192 

193 def update_feature (feature, mc) 

194 

195 check if mc.user is allowed to write to this model 

196 

197 attach an empty record to feature 

198 

199 open a transaction in the source 

200 

201 for each field in this model 

202 invoke the "before_update" callback 

203 to transfer data from feature.attributes to feature.record 

204 

205 write changes to the source, using feature.uid as a key and feature.record as data 

206 (e.g. UPDATE source SET ...record... WHERE id=feature.uid) 

207 

208 for each field in this model 

209 invoke the "after_update" callback 

210 to synchronize updated data, e.g. update a linked model 

211 

212 commit the transaction 

213 

214Examples 

215-------- 

216 

217An editable database model with a value and a validator:: 

218 

219 models+ { 

220 type "postgres" 

221 tableName "edit.poi" 

222 isEditable true 

223 permissions.edit "allow all" 

224 

225 fields+ { name "id" type "integer" isPrimaryKey true permissions.edit "deny all" } 

226 fields+ { name "name" type "text" isRequired true widget { type "input" } } 

227 fields+ { 

228 name "updated" 

229 type "datetime" 

230 values+ { type "currentTimestamp" forRead false } 

231 } 

232 fields+ { name "geom" type "geometry" } 

233 } 

234 

235Reading features of a model in Python:: 

236 

237 mc = gws.ModelContext(op=gws.ModelOperation.read, target=gws.ModelReadTarget.map, user=user) 

238 features = model.find_features(gws.SearchQuery(uids=['1', '2']), mc) 

239 props = [model.feature_to_props(f, mc) for f in features] 

240""" 

241 

242from .core import Config, Object, Props 

243 

244from . import manager, default_model, util, field, related_field 

245 

246from .util import ( 

247 iter_features, 

248 copy_context, 

249 secondary_context, 

250)