Coverage for gws-app/gws/base/model/scalar_field.py: 83%
71 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-05 13:35 +0200
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-05 13:35 +0200
1"""Base class for scalar fields."""
3from typing import Optional, Callable, cast
5import gws
6import gws.base.model.field
9class Config(gws.base.model.field.Config):
10 """Configuration for the scalar field."""
12 isVirtual: Optional[bool]
13 """The field is not read from or written to the database."""
16class Props(gws.base.model.field.Props):
17 pass
20class Object(gws.base.model.field.Object):
21 """Base scalar field.
23 Maps the field to the source column with the same name. Provides reading the column
24 and writing it to the record, and the transfer of the value between record, feature
25 and props, applying value objects and field permissions.
26 """
28 isVirtual: bool
29 """The field is not read from or written to the database."""
31 def configure(self):
32 self.isVirtual = self.cfg('isVirtual', default=False)
34 def before_select(self, mc):
35 if self.isVirtual:
36 return
37 model = cast(gws.DatabaseModel, self.model)
38 mc.dbSelect.columns.append(model.column(self.name))
40 def after_select(self, features, mc):
41 for feature in features:
42 self.from_record(feature, mc)
44 def before_create(self, feature, mc):
45 if self.isAuto:
46 return
47 if self.isVirtual:
48 return
49 self.to_record(feature, mc)
51 def before_update(self, feature, mc):
52 if self.isAuto:
53 return
54 if self.isVirtual:
55 return
56 self.to_record(feature, mc)
58 ##
60 def from_props(self, feature, mc):
61 value = feature.props.attributes.get(self.name)
62 if value is not None:
63 value = self.prop_to_python(feature, value, mc)
64 if value is not None:
65 feature.set(self.name, value)
67 def to_props(self, feature, mc):
68 if not mc.user.can_read(self):
69 return
70 value = feature.get(self.name)
71 if value is not None:
72 value = self.python_to_prop(feature, value, mc)
73 if value is not None:
74 feature.props.attributes[self.name] = value
76 ##
78 def do_init(self, feature, mc):
79 value = self.get_value(
80 feature,
81 {},
82 mc.user.can_read(self),
83 self.prop_to_python,
84 mc,
85 )
86 if value is not None:
87 feature.set(self.name, value)
89 ##
91 def from_record(self, feature, mc):
92 value = self.get_value(
93 feature,
94 feature.record.attributes,
95 mc.user.can_read(self),
96 self.raw_to_python,
97 mc,
98 )
99 if value is not None:
100 feature.set(self.name, value)
102 def to_record(self, feature, mc):
103 value = self.get_value(
104 feature,
105 feature.attributes,
106 mc.user.can_write(self),
107 self.python_to_raw,
108 mc,
109 )
110 if value is not None:
111 feature.record.attributes[self.name] = value
113 def get_value(
114 self,
115 feature: gws.Feature,
116 source: dict,
117 has_access: bool,
118 convert_fn: Callable,
119 mc: gws.ModelContext,
120 ):
121 """Compute the field value for the current operation.
123 A value object that is not a default value provides the value. Otherwise the value
124 is taken from ``source`` and converted, if the user has access and the value is not
125 None. Otherwise a default value object provides the value.
127 Args:
128 feature: The feature.
129 source: A dict of attributes to read the value from.
130 has_access: Whether the user may access the field.
131 convert_fn: Function ``(feature, value, mc)`` that converts the source value.
132 mc: The model context.
134 Returns:
135 The value, or None if there is none.
136 """
137 mv = self.model_value(mc)
139 if mv and not mv.isDefault:
140 return mv.compute(self, feature, mc)
142 if has_access and self.name in source:
143 value = source.get(self.name)
144 if value is not None:
145 return convert_fn(feature, value, mc)
147 if mv:
148 return mv.compute(self, feature, mc)
150 def model_value(self, mc: gws.ModelContext):
151 """Return the first value object for the current operation that the user can use.
153 Args:
154 mc: The model context.
156 Returns:
157 The value object, or None if there is none.
158 """
159 for mv in self.values:
160 if mc.op in mv.ops and mc.user.can_use(mv):
161 return mv