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 22:59 +0200

1from typing import Optional, cast 

2 

3import gws 

4 

5 

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] 

15 

16 

17class Config(gws.ConfigWithAccess): 

18 """Configuration for the model field.""" 

19 

20 name: str 

21 """The name of the field.""" 

22 title: Optional[str] 

23 """The title of the field.""" 

24 

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)""" 

35 

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.""" 

40 

41 widget: Optional[gws.ext.config.modelWidget] 

42 """Configuration for the field widget.""" 

43 

44 

45## 

46 

47 

48class Object(gws.ModelField): 

49 notEmptyValidator: gws.ModelValidator 

50 formatValidator: gws.ModelValidator 

51 

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) 

56 

57 self.values = [] 

58 self.validators = [] 

59 self.widget = None 

60 

61 self.configure_flags() 

62 self.configure_values() 

63 self.configure_validators() 

64 self.configure_widget() 

65 

66 def configure_flags(self): 

67 col = self.describe() 

68 

69 p = self.cfg('isPrimaryKey') 

70 if p is not None: 

71 self.isPrimaryKey = p 

72 else: 

73 self.isPrimaryKey = col and col.isPrimaryKey 

74 

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 

80 

81 p = self.cfg('isAuto') 

82 if p is not None: 

83 self.isAuto = p 

84 else: 

85 self.isAuto = col and col.isAutoincrement 

86 

87 p = self.cfg('isUnique') 

88 if p is not None: 

89 self.isUnique = p 

90 else: 

91 self.isUnique = False 

92 

93 self.isHidden = self.cfg('isHidden', default=False) 

94 

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 

100 

101 def configure_validators(self): 

102 vd_not_empty = None 

103 vd_format = None 

104 

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) 

113 

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 ) 

121 

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 ) 

129 

130 return True 

131 

132 def create_validator(self, cfg): 

133 return self.create_child(gws.ext.object.modelValidator, cfg) 

134 

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 

140 

141 ## 

142 

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 

149 

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 ) 

159 

160 ## 

161 

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) 

165 

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 

176 

177 # apply the 'format' validator 

178 

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 

188 

189 # apply others 

190 

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 

202 

203 def related_models(self): 

204 return [] 

205 

206 def find_relatable_features(self, search, mc): 

207 return [] 

208 

209 def raw_to_python(self, feature, value, mc): 

210 return value 

211 

212 def prop_to_python(self, feature, value, mc): 

213 return value 

214 

215 def python_to_raw(self, feature, value, mc): 

216 return value 

217 

218 def python_to_prop(self, feature, value, mc): 

219 return value 

220 

221 ## 

222 

223 def describe(self): 

224 desc = self.model.describe() 

225 if desc: 

226 return desc.columnMap.get(self.name)