Coverage for gws-app/gws/base/model/field.py: 82%
129 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-16 23:09 +0200
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-16 23:09 +0200
1from typing import Optional, cast
3import gws
6class Props(gws.Props):
7 attributeType: gws.AttributeType
8 geometryType: gws.GeometryType
9 name: str
10 title: str
11 type: str
12 widget: gws.ext.props.modelWidget
13 uid: str
14 relatedModelUids: list[str]
17class Config(gws.ConfigWithAccess):
18 """Configuration for the model field."""
20 name: str
21 """The name of the field."""
22 title: Optional[str]
23 """The title of the field."""
25 isPrimaryKey: Optional[bool]
26 """If True, the field is a primary key."""
27 isRequired: Optional[bool]
28 """If True, the field is required."""
29 isUnique: Optional[bool]
30 """If True, the field is unique."""
31 isAuto: Optional[bool]
32 """If True, the field is auto-updated."""
33 isHidden: Optional[bool]
34 """If True, the field is not automatically displayed in UIs. (added in 8.2)"""
36 values: Optional[list[gws.ext.config.modelValue]]
37 """List of possible values for the field."""
38 validators: Optional[list[gws.ext.config.modelValidator]]
39 """List of validators for the field."""
41 widget: Optional[gws.ext.config.modelWidget]
42 """Configuration for the field widget."""
45##
48class Object(gws.ModelField):
49 notEmptyValidator: gws.ModelValidator
50 formatValidator: gws.ModelValidator
52 def configure(self):
53 self.model = self.cfg('_defaultModel')
54 self.name = self.cfg('name')
55 self.title = self.cfg('title', default=self.name)
57 self.values = []
58 self.validators = []
59 self.widget = None
61 self.configure_flags()
62 self.configure_values()
63 self.configure_validators()
64 self.configure_widget()
66 def configure_flags(self):
67 col = self.describe()
69 p = self.cfg('isPrimaryKey')
70 if p is not None:
71 self.isPrimaryKey = p
72 else:
73 self.isPrimaryKey = col and col.isPrimaryKey
75 p = self.cfg('isRequired')
76 if p is not None:
77 self.isRequired = p
78 else:
79 self.isRequired = col and not col.isNullable
81 p = self.cfg('isAuto')
82 if p is not None:
83 self.isAuto = p
84 else:
85 self.isAuto = col and col.isAutoincrement
87 p = self.cfg('isUnique')
88 if p is not None:
89 self.isUnique = p
90 else:
91 self.isUnique = False
93 self.isHidden = self.cfg('isHidden', default=False)
95 def configure_values(self):
96 p = self.cfg('values')
97 if p:
98 self.values = self.create_children(gws.ext.object.modelValue, p)
99 return True
101 def configure_validators(self):
102 vd_not_empty = None
103 vd_format = None
105 for p in self.cfg('validators', default=[]):
106 vd = self.create_validator(p)
107 if vd.extType == 'notEmpty':
108 vd_not_empty = vd
109 elif vd.extType == 'format':
110 vd_format = vd
111 else:
112 self.validators.append(vd)
114 self.notEmptyValidator = vd_not_empty or self.root.create_shared(
115 gws.ext.object.modelValidator,
116 type='notEmpty',
117 uid='gws.base.model.field.default_validator_notEmpty',
118 forCreate=True,
119 forUpdate=True,
120 )
122 self.formatValidator = vd_format or self.root.create_shared(
123 gws.ext.object.modelValidator,
124 type='format',
125 uid='gws.base.model.field.default_validator_format',
126 forCreate=True,
127 forUpdate=True,
128 )
130 return True
132 def create_validator(self, cfg):
133 return self.create_child(gws.ext.object.modelValidator, cfg)
135 def configure_widget(self):
136 p = self.cfg('widget')
137 if p:
138 self.widget = self.create_child(gws.ext.object.modelWidget, p)
139 return True
141 ##
143 def props(self, user):
144 wp = None
145 if self.widget:
146 wp = gws.props_of(self.widget, user, self)
147 if not user.can_write(self):
148 wp.readOnly = True
150 return Props(
151 attributeType=self.attributeType,
152 name=self.name,
153 title=self.title,
154 type=self.extType,
155 widget=wp,
156 uid=self.uid,
157 relatedModelUids=[m.uid for m in self.related_models() if user.can_read(m)],
158 )
160 ##
162 def do_validate(self, feature, mc):
163 # apply the 'notEmpty' validator and exit immediately if it fails
164 # (no error message if field is not required)
166 ok = self.notEmptyValidator.validate(self, feature, mc)
167 if not ok:
168 if self.isRequired:
169 feature.errors.append(
170 gws.ModelValidationError(
171 fieldName=self.name,
172 message=self.notEmptyValidator.message,
173 )
174 )
175 return
177 # apply the 'format' validator
179 ok = self.formatValidator.validate(self, feature, mc)
180 if not ok:
181 feature.errors.append(
182 gws.ModelValidationError(
183 fieldName=self.name,
184 message=self.formatValidator.message,
185 )
186 )
187 return
189 # apply others
191 for vd in self.validators:
192 if mc.op in vd.ops:
193 ok = vd.validate(self, feature, mc)
194 if not ok:
195 feature.errors.append(
196 gws.ModelValidationError(
197 fieldName=self.name,
198 message=vd.message,
199 )
200 )
201 return
203 def related_models(self):
204 return []
206 def find_relatable_features(self, search, mc):
207 return []
209 def raw_to_python(self, feature, value, mc):
210 return value
212 def prop_to_python(self, feature, value, mc):
213 return value
215 def python_to_raw(self, feature, value, mc):
216 return value
218 def python_to_prop(self, feature, value, mc):
219 return value
221 ##
223 def describe(self):
224 desc = self.model.describe()
225 if desc:
226 return desc.columnMap.get(self.name)