Coverage for gws-app/gws/plugin/model_field/related_feature/__init__.py: 83%

65 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-10-16 23:09 +0200

1"""Related Feature field 

2 

3Represents a child->parent M:1 relationship to another model:: 

4 

5 +-------------+ +--------------+ 

6 | model | | toModel | 

7 +-------------+ +--------------+ 

8 | fromKey |-------->| toKey | 

9 +-------------+ +--------------+ 

10 

11The value of the field is the parent feature. 

12""" 

13 

14import gws 

15import gws.base.model 

16import gws.base.model.related_field as related_field 

17import gws.lib.sa as sa 

18 

19gws.ext.new.modelField('relatedFeature') 

20 

21 

22class Config(related_field.Config): 

23 """Configuration for related feature field.""" 

24 

25 fromColumn: str 

26 """Foreign key column in this table.""" 

27 toModel: str 

28 """Related model.""" 

29 toColumn: str = '' 

30 """Key column in the related model, primary key by default.""" 

31 

32 

33class Props(related_field.Props): 

34 pass 

35 

36 

37class Object(related_field.Object): 

38 attributeType = gws.AttributeType.feature 

39 

40 def configure_relationship(self): 

41 to_mod = self.get_model(self.cfg('toModel')) 

42 

43 self.rel = related_field.Relationship( 

44 src=related_field.RelRef( 

45 model=self.model, 

46 table=self.model.table(), 

47 key=self.model.column(self.cfg('fromColumn')), 

48 uid=self.model.uid_column(), 

49 ), 

50 tos=[ 

51 related_field.RelRef( 

52 model=to_mod, 

53 table=to_mod.table(), 

54 key=self.column_or_uid(to_mod, self.cfg('toColumn')), 

55 uid=to_mod.uid_column(), 

56 ) 

57 ], 

58 ) 

59 self.rel.to = self.rel.tos[0] 

60 

61 ## 

62 

63 def do_init(self, feature, mc): 

64 key = feature.record.attributes.get(self.rel.src.key.name) 

65 if key: 

66 to_uids = self.uids_for_key(self.rel.to, key, mc) 

67 to_features = self.rel.to.model.get_features( 

68 to_uids, 

69 gws.base.model.secondary_context(mc), 

70 ) 

71 if to_features: 

72 feature.attributes[self.name] = to_features[0] 

73 

74 def after_create_related(self, to_feature, mc): 

75 if to_feature.model != self.rel.to.model: 

76 return 

77 

78 for feature in to_feature.createWithFeatures: 

79 if feature.model == self.model: 

80 key = self.key_for_uid( 

81 self.rel.to.model, 

82 self.rel.to.key, 

83 to_feature.insertedPrimaryKey, 

84 mc, 

85 ) 

86 if key: 

87 self.update_key_for_uids( 

88 self.model, 

89 self.rel.src.key, 

90 [feature.uid()], 

91 key, 

92 mc, 

93 ) 

94 

95 def uids_for_key(self, rel: related_field.RelRef, key, mc): 

96 sql = sa.select(rel.uid).where(rel.key.__eq__(key)) 

97 with rel.model.db.connect() as conn: 

98 return set(str(u) for u in conn.execute(sql)) 

99 

100 def after_select(self, features, mc): 

101 if not mc.user.can_read(self) or mc.relDepth >= mc.maxDepth: 

102 return 

103 

104 uid_to_f = {f.uid(): f for f in features} 

105 

106 sql = ( 

107 sa.select( 

108 self.rel.to.uid, 

109 self.rel.src.uid, 

110 ) 

111 .select_from( 

112 self.rel.to.table.join( 

113 self.rel.src.table, 

114 self.rel.src.key.__eq__(self.rel.to.key), 

115 ), 

116 ) 

117 .where(self.rel.src.uid.in_(uid_to_f)) 

118 ) 

119 

120 r_to_uids = {} 

121 with self.model.db.connect() as conn: 

122 for r, u in conn.execute(sql): 

123 r_to_uids.setdefault(str(r), []).append(str(u)) 

124 

125 for to_feature in self.rel.to.model.get_features( 

126 r_to_uids, 

127 gws.base.model.secondary_context(mc), 

128 ): 

129 for uid in r_to_uids.get(to_feature.uid(), []): 

130 feature = uid_to_f.get(uid) 

131 feature.set(self.name, to_feature) 

132 

133 def before_create(self, feature, mc): 

134 self.before_write(feature, mc) 

135 

136 def before_update(self, feature, mc): 

137 self.before_write(feature, mc) 

138 

139 def before_write(self, feature: gws.Feature, mc: gws.ModelContext): 

140 if not mc.user.can_write(self): 

141 return 

142 

143 if feature.has(self.name): 

144 key = None 

145 to_feature = feature.get(self.name) 

146 if to_feature: 

147 key = self.key_for_uid( 

148 self.rel.to.model, 

149 self.rel.to.key, 

150 to_feature.uid(), 

151 mc, 

152 ) 

153 feature.record.attributes[self.rel.src.key.name] = key