Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/django/db/models/fields/related_lookups.py: 62%

91 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1from django.db.models.lookups import ( 

2 Exact, 

3 GreaterThan, 

4 GreaterThanOrEqual, 

5 In, 

6 IsNull, 

7 LessThan, 

8 LessThanOrEqual, 

9) 

10 

11 

12class MultiColSource: 

13 contains_aggregate = False 

14 

15 def __init__(self, alias, targets, sources, field): 

16 self.targets, self.sources, self.field, self.alias = ( 

17 targets, 

18 sources, 

19 field, 

20 alias, 

21 ) 

22 self.output_field = self.field 

23 

24 def __repr__(self): 

25 return "{}({}, {})".format(self.__class__.__name__, self.alias, self.field) 

26 

27 def relabeled_clone(self, relabels): 

28 return self.__class__( 

29 relabels.get(self.alias, self.alias), self.targets, self.sources, self.field 

30 ) 

31 

32 def get_lookup(self, lookup): 

33 return self.output_field.get_lookup(lookup) 

34 

35 def resolve_expression(self, *args, **kwargs): 

36 return self 

37 

38 

39def get_normalized_value(value, lhs): 

40 from django.db.models import Model 

41 

42 if isinstance(value, Model): 

43 value_list = [] 

44 sources = lhs.output_field.get_path_info()[-1].target_fields 

45 for source in sources: 

46 while not isinstance(value, source.model) and source.remote_field: 46 ↛ 47line 46 didn't jump to line 47, because the condition on line 46 was never true

47 source = source.remote_field.model._meta.get_field( 

48 source.remote_field.field_name 

49 ) 

50 try: 

51 value_list.append(getattr(value, source.attname)) 

52 except AttributeError: 

53 # A case like Restaurant.objects.filter(place=restaurant_instance), 

54 # where place is a OneToOneField and the primary key of Restaurant. 

55 return (value.pk,) 

56 return tuple(value_list) 

57 if not isinstance(value, tuple): 57 ↛ 59line 57 didn't jump to line 59, because the condition on line 57 was never false

58 return (value,) 

59 return value 

60 

61 

62class RelatedIn(In): 

63 def get_prep_lookup(self): 

64 if not isinstance(self.lhs, MultiColSource) and self.rhs_is_direct_value(): 

65 # If we get here, we are dealing with single-column relations. 

66 self.rhs = [get_normalized_value(val, self.lhs)[0] for val in self.rhs] 

67 # We need to run the related field's get_prep_value(). Consider case 

68 # ForeignKey to IntegerField given value 'abc'. The ForeignKey itself 

69 # doesn't have validation for non-integers, so we must run validation 

70 # using the target field. 

71 if hasattr(self.lhs.output_field, "get_path_info"): 71 ↛ 78line 71 didn't jump to line 78, because the condition on line 71 was never false

72 # Run the target field's get_prep_value. We can safely assume there is 

73 # only one as we don't get to the direct value branch otherwise. 

74 target_field = self.lhs.output_field.get_path_info()[-1].target_fields[ 

75 -1 

76 ] 

77 self.rhs = [target_field.get_prep_value(v) for v in self.rhs] 

78 return super().get_prep_lookup() 

79 

80 def as_sql(self, compiler, connection): 

81 if isinstance(self.lhs, MultiColSource): 81 ↛ 86line 81 didn't jump to line 86, because the condition on line 81 was never true

82 # For multicolumn lookups we need to build a multicolumn where clause. 

83 # This clause is either a SubqueryConstraint (for values that need 

84 # to be compiled to SQL) or an OR-combined list of 

85 # (col1 = val1 AND col2 = val2 AND ...) clauses. 

86 from django.db.models.sql.where import ( 

87 AND, 

88 OR, 

89 SubqueryConstraint, 

90 WhereNode, 

91 ) 

92 

93 root_constraint = WhereNode(connector=OR) 

94 if self.rhs_is_direct_value(): 

95 values = [get_normalized_value(value, self.lhs) for value in self.rhs] 

96 for value in values: 

97 value_constraint = WhereNode() 

98 for source, target, val in zip( 

99 self.lhs.sources, self.lhs.targets, value 

100 ): 

101 lookup_class = target.get_lookup("exact") 

102 lookup = lookup_class( 

103 target.get_col(self.lhs.alias, source), val 

104 ) 

105 value_constraint.add(lookup, AND) 

106 root_constraint.add(value_constraint, OR) 

107 else: 

108 root_constraint.add( 

109 SubqueryConstraint( 

110 self.lhs.alias, 

111 [target.column for target in self.lhs.targets], 

112 [source.name for source in self.lhs.sources], 

113 self.rhs, 

114 ), 

115 AND, 

116 ) 

117 return root_constraint.as_sql(compiler, connection) 

118 else: 

119 if not getattr(self.rhs, "has_select_fields", True) and not getattr( 119 ↛ 122line 119 didn't jump to line 122, because the condition on line 119 was never true

120 self.lhs.field.target_field, "primary_key", False 

121 ): 

122 self.rhs.clear_select_clause() 

123 if ( 

124 getattr(self.lhs.output_field, "primary_key", False) 

125 and self.lhs.output_field.model == self.rhs.model 

126 ): 

127 # A case like Restaurant.objects.filter(place__in=restaurant_qs), 

128 # where place is a OneToOneField and the primary key of 

129 # Restaurant. 

130 target_field = self.lhs.field.name 

131 else: 

132 target_field = self.lhs.field.target_field.name 

133 self.rhs.add_fields([target_field], True) 

134 return super().as_sql(compiler, connection) 

135 

136 

137class RelatedLookupMixin: 

138 def get_prep_lookup(self): 

139 if not isinstance(self.lhs, MultiColSource) and not hasattr( 139 ↛ 156line 139 didn't jump to line 156, because the condition on line 139 was never false

140 self.rhs, "resolve_expression" 

141 ): 

142 # If we get here, we are dealing with single-column relations. 

143 self.rhs = get_normalized_value(self.rhs, self.lhs)[0] 

144 # We need to run the related field's get_prep_value(). Consider case 

145 # ForeignKey to IntegerField given value 'abc'. The ForeignKey itself 

146 # doesn't have validation for non-integers, so we must run validation 

147 # using the target field. 

148 if self.prepare_rhs and hasattr(self.lhs.output_field, "get_path_info"): 

149 # Get the target field. We can safely assume there is only one 

150 # as we don't get to the direct value branch otherwise. 

151 target_field = self.lhs.output_field.get_path_info()[-1].target_fields[ 

152 -1 

153 ] 

154 self.rhs = target_field.get_prep_value(self.rhs) 

155 

156 return super().get_prep_lookup() 

157 

158 def as_sql(self, compiler, connection): 

159 if isinstance(self.lhs, MultiColSource): 159 ↛ 160line 159 didn't jump to line 160, because the condition on line 159 was never true

160 assert self.rhs_is_direct_value() 

161 self.rhs = get_normalized_value(self.rhs, self.lhs) 

162 from django.db.models.sql.where import AND, WhereNode 

163 

164 root_constraint = WhereNode() 

165 for target, source, val in zip( 

166 self.lhs.targets, self.lhs.sources, self.rhs 

167 ): 

168 lookup_class = target.get_lookup(self.lookup_name) 

169 root_constraint.add( 

170 lookup_class(target.get_col(self.lhs.alias, source), val), AND 

171 ) 

172 return root_constraint.as_sql(compiler, connection) 

173 return super().as_sql(compiler, connection) 

174 

175 

176class RelatedExact(RelatedLookupMixin, Exact): 

177 pass 

178 

179 

180class RelatedLessThan(RelatedLookupMixin, LessThan): 

181 pass 

182 

183 

184class RelatedGreaterThan(RelatedLookupMixin, GreaterThan): 

185 pass 

186 

187 

188class RelatedGreaterThanOrEqual(RelatedLookupMixin, GreaterThanOrEqual): 

189 pass 

190 

191 

192class RelatedLessThanOrEqual(RelatedLookupMixin, LessThanOrEqual): 

193 pass 

194 

195 

196class RelatedIsNull(RelatedLookupMixin, IsNull): 

197 pass