If the update depends on results from other tables, munge the "where" conditions to match the format required for (portable) SQL updates. If multiple updates are required, pull out the id values to update at this point so that they don't change as a result of the pr
(self)
| 2164 | return list(rows) |
| 2165 | |
| 2166 | def pre_sql_setup(self): |
| 2167 | """ |
| 2168 | If the update depends on results from other tables, munge the "where" |
| 2169 | conditions to match the format required for (portable) SQL updates. |
| 2170 | |
| 2171 | If multiple updates are required, pull out the id values to update at |
| 2172 | this point so that they don't change as a result of the progressive |
| 2173 | updates. |
| 2174 | """ |
| 2175 | refcounts_before = self.query.alias_refcount.copy() |
| 2176 | # Ensure base table is in the query |
| 2177 | self.query.get_initial_alias() |
| 2178 | count = self.query.count_active_tables() |
| 2179 | if not self.query.related_updates and count == 1: |
| 2180 | return |
| 2181 | query = self.query.chain(klass=Query) |
| 2182 | query.select_related = False |
| 2183 | query.clear_ordering(force=True) |
| 2184 | query.extra = {} |
| 2185 | query.select = [] |
| 2186 | meta = query.get_meta() |
| 2187 | fields = [meta.pk.name] |
| 2188 | related_ids_index = [] |
| 2189 | for related in self.query.related_updates: |
| 2190 | if all( |
| 2191 | path.join_field.primary_key for path in meta.get_path_to_parent(related) |
| 2192 | ): |
| 2193 | # If a primary key chain exists to the targeted related update, |
| 2194 | # then the meta.pk value can be used for it. |
| 2195 | related_ids_index.append((related, 0)) |
| 2196 | else: |
| 2197 | # This branch will only be reached when updating a field of an |
| 2198 | # ancestor that is not part of the primary key chain of a MTI |
| 2199 | # tree. |
| 2200 | related_ids_index.append((related, len(fields))) |
| 2201 | fields.append(related._meta.pk.name) |
| 2202 | query.add_fields(fields) |
| 2203 | super().pre_sql_setup() |
| 2204 | |
| 2205 | is_composite_pk = meta.is_composite_pk |
| 2206 | must_pre_select = ( |
| 2207 | count > 1 and not self.connection.features.update_can_self_select |
| 2208 | ) |
| 2209 | |
| 2210 | # Now we adjust the current query: reset the where clause and get rid |
| 2211 | # of all the tables we don't need (since they're in the sub-select). |
| 2212 | self.query.clear_where() |
| 2213 | if self.query.related_updates or must_pre_select: |
| 2214 | # Either we're using the idents in multiple update queries (so |
| 2215 | # don't want them to change), or the db backend doesn't support |
| 2216 | # selecting from the updating table (e.g. MySQL). |
| 2217 | idents = [] |
| 2218 | related_ids = collections.defaultdict(list) |
| 2219 | for rows in query.get_compiler(self.using).execute_sql(MULTI): |
| 2220 | pks = [row if is_composite_pk else row[0] for row in rows] |
| 2221 | idents.extend(pks) |
| 2222 | for parent, index in related_ids_index: |
| 2223 | related_ids[parent].extend(r[index] for r in rows) |
no test coverage detected