Coverage for gws-app/gws/plugin/model_field/related_feature/__init__.py: 0%
66 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-17 01:37 +0200
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-17 01:37 +0200
1"""Related Feature field
3Represents a child->parent M:1 relationship to another model::
5 +-------------+ +--------------+
6 | model | | toModel |
7 +-------------+ +--------------+
8 | fromKey |-------->| toKey |
9 +-------------+ +--------------+
11The value of the field is the parent feature.
12"""
14import gws
15import gws.base.database.model
16import gws.base.model
17import gws.base.model.related_field as related_field
18import gws.lib.sa as sa
20gws.ext.new.modelField('relatedFeature')
23class Config(related_field.Config):
24 fromColumn: str
25 """foreign key column in this table"""
26 toModel: str
27 """related model"""
28 toColumn: str = ''
29 """key column in the related model, primary key by default"""
32class Props(related_field.Props):
33 pass
36class Object(related_field.Object):
37 attributeType = gws.AttributeType.feature
39 def configure_relationship(self):
40 to_mod = self.get_model(self.cfg('toModel'))
42 self.rel = related_field.Relationship(
43 src=related_field.RelRef(
44 model=self.model,
45 table=self.model.table(),
46 key=self.model.column(self.cfg('fromColumn')),
47 uid=self.model.uid_column(),
48 ),
49 tos=[
50 related_field.RelRef(
51 model=to_mod,
52 table=to_mod.table(),
53 key=self.column_or_uid(to_mod, self.cfg('toColumn')),
54 uid=to_mod.uid_column(),
55 )
56 ]
57 )
58 self.rel.to = self.rel.tos[0]
60 ##
62 def do_init(self, feature, mc):
63 key = feature.record.attributes.get(self.rel.src.key.name)
64 if key:
65 to_uids = self.uids_for_key(self.rel.to, key, mc)
66 to_features = self.rel.to.model.get_features(to_uids, gws.base.model.secondary_context(mc))
67 if to_features:
68 feature.attributes[self.name] = to_features[0]
70 def after_create_related(self, to_feature, mc):
71 if to_feature.model != self.rel.to.model:
72 return
74 for feature in to_feature.createWithFeatures:
75 if feature.model == self.model:
76 key = self.key_for_uid(self.rel.to.model, self.rel.to.key, to_feature.insertedPrimaryKey, mc)
77 if key:
78 self.update_key_for_uids(self.model, self.rel.src.key, [feature.uid()], key, mc)
80 def uids_for_key(self, rel: related_field.RelRef, key, mc):
81 sql = sa.select(rel.uid).where(rel.key.__eq__(key))
82 with rel.model.db.connect() as conn:
83 return set(str(u) for u in conn.execute(sql))
85 def after_select(self, features, mc):
86 if not mc.user.can_read(self) or mc.relDepth >= mc.maxDepth:
87 return
89 uid_to_f = {f.uid(): f for f in features}
91 sql = sa.select(
92 self.rel.to.uid,
93 self.rel.src.uid,
94 ).select_from(
95 self.rel.to.table.join(
96 self.rel.src.table, self.rel.src.key.__eq__(self.rel.to.key)
97 )
98 ).where(
99 self.rel.src.uid.in_(uid_to_f)
100 )
102 r_to_uids = {}
103 with self.model.db.connect() as conn:
104 for r, u in conn.execute(sql):
105 r_to_uids.setdefault(str(r), []).append(str(u))
107 for to_feature in self.rel.to.model.get_features(r_to_uids, gws.base.model.secondary_context(mc)):
108 for uid in r_to_uids.get(to_feature.uid(), []):
109 feature = uid_to_f.get(uid)
110 feature.set(self.name, to_feature)
112 def before_create(self, feature, mc):
113 self.before_write(feature, mc)
115 def before_update(self, feature, mc):
116 self.before_write(feature, mc)
118 def before_write(self, feature: gws.Feature, mc: gws.ModelContext):
119 if not mc.user.can_write(self):
120 return
122 if feature.has(self.name):
123 key = None
124 to_feature = feature.get(self.name)
125 if to_feature:
126 key = self.key_for_uid(self.rel.to.model, self.rel.to.key, to_feature.uid(), mc)
127 feature.record.attributes[self.rel.src.key.name] = key