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

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.database.model 

16import gws.base.model 

17import gws.base.model.related_field as related_field 

18import gws.lib.sa as sa 

19 

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

21 

22 

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

30 

31 

32class Props(related_field.Props): 

33 pass 

34 

35 

36class Object(related_field.Object): 

37 attributeType = gws.AttributeType.feature 

38 

39 def configure_relationship(self): 

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

41 

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] 

59 

60 ## 

61 

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] 

69 

70 def after_create_related(self, to_feature, mc): 

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

72 return 

73 

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) 

79 

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

84 

85 def after_select(self, features, mc): 

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

87 return 

88 

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

90 

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 ) 

101 

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

106 

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) 

111 

112 def before_create(self, feature, mc): 

113 self.before_write(feature, mc) 

114 

115 def before_update(self, feature, mc): 

116 self.before_write(feature, mc) 

117 

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

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

120 return 

121 

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