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
« 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)
12class MultiColSource:
13 contains_aggregate = False
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
24 def __repr__(self):
25 return "{}({}, {})".format(self.__class__.__name__, self.alias, self.field)
27 def relabeled_clone(self, relabels):
28 return self.__class__(
29 relabels.get(self.alias, self.alias), self.targets, self.sources, self.field
30 )
32 def get_lookup(self, lookup):
33 return self.output_field.get_lookup(lookup)
35 def resolve_expression(self, *args, **kwargs):
36 return self
39def get_normalized_value(value, lhs):
40 from django.db.models import Model
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
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()
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 )
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)
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)
156 return super().get_prep_lookup()
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
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)
176class RelatedExact(RelatedLookupMixin, Exact):
177 pass
180class RelatedLessThan(RelatedLookupMixin, LessThan):
181 pass
184class RelatedGreaterThan(RelatedLookupMixin, GreaterThan):
185 pass
188class RelatedGreaterThanOrEqual(RelatedLookupMixin, GreaterThanOrEqual):
189 pass
192class RelatedLessThanOrEqual(RelatedLookupMixin, LessThanOrEqual):
193 pass
196class RelatedIsNull(RelatedLookupMixin, IsNull):
197 pass