Take a list of 2-tuples of the form (migration instance, True) and unapply them in reverse order they occur in the full_plan. Since unapplying a migration requires the project state prior to that migration, Django will compute the migration states before each of the
(self, plan, full_plan, fake)
| 174 | return state |
| 175 | |
| 176 | def _migrate_all_backwards(self, plan, full_plan, fake): |
| 177 | """ |
| 178 | Take a list of 2-tuples of the form (migration instance, True) and |
| 179 | unapply them in reverse order they occur in the full_plan. |
| 180 | |
| 181 | Since unapplying a migration requires the project state prior to that |
| 182 | migration, Django will compute the migration states before each of them |
| 183 | in a first run over the plan and then unapply them in a second run over |
| 184 | the plan. |
| 185 | """ |
| 186 | migrations_to_run = {m[0] for m in plan} |
| 187 | # Holds all migration states prior to the migrations being unapplied |
| 188 | states = {} |
| 189 | state = self._create_project_state() |
| 190 | applied_migrations = { |
| 191 | self.loader.graph.nodes[key] |
| 192 | for key in self.loader.applied_migrations |
| 193 | if key in self.loader.graph.nodes |
| 194 | } |
| 195 | if self.progress_callback: |
| 196 | self.progress_callback("render_start") |
| 197 | for migration, _ in full_plan: |
| 198 | if not migrations_to_run: |
| 199 | # We remove every migration that we applied from this set so |
| 200 | # that we can bail out once the last migration has been applied |
| 201 | # and don't always run until the very end of the migration |
| 202 | # process. |
| 203 | break |
| 204 | if migration in migrations_to_run: |
| 205 | if "apps" not in state.__dict__: |
| 206 | state.apps # Render all -- performance critical |
| 207 | # The state before this migration |
| 208 | states[migration] = state |
| 209 | # The old state keeps as-is, we continue with the new state |
| 210 | state = migration.mutate_state(state, preserve=True) |
| 211 | migrations_to_run.remove(migration) |
| 212 | elif migration in applied_migrations: |
| 213 | # Only mutate the state if the migration is actually applied |
| 214 | # to make sure the resulting state doesn't include changes |
| 215 | # from unrelated migrations. |
| 216 | migration.mutate_state(state, preserve=False) |
| 217 | if self.progress_callback: |
| 218 | self.progress_callback("render_success") |
| 219 | |
| 220 | for migration, _ in plan: |
| 221 | self.unapply_migration(states[migration], migration, fake=fake) |
| 222 | applied_migrations.remove(migration) |
| 223 | |
| 224 | # Generate the post migration state by starting from the state before |
| 225 | # the last migration is unapplied and mutating it to include all the |
| 226 | # remaining applied migrations. |
| 227 | last_unapplied_migration = plan[-1][0] |
| 228 | state = states[last_unapplied_migration] |
| 229 | # Avoid mutating state with apps rendered as it's an expensive |
| 230 | # operation. |
| 231 | del state.apps |
| 232 | for index, (migration, _) in enumerate(full_plan): |
| 233 | if migration == last_unapplied_migration: |
no test coverage detected