| 83 | return state |
| 84 | |
| 85 | async def unapply( |
| 86 | self, |
| 87 | state: State, |
| 88 | *, |
| 89 | dry_run: bool = False, |
| 90 | schema_editor: BaseSchemaEditor | None = None, |
| 91 | collect_sql: bool = False, |
| 92 | ) -> State: |
| 93 | supports_transactions = ( |
| 94 | schema_editor is not None and schema_editor.client.capabilities.supports_transactions |
| 95 | ) |
| 96 | need_old_state = (collect_sql and schema_editor) or ( |
| 97 | not dry_run and schema_editor is not None |
| 98 | ) |
| 99 | to_run: list[tuple[Operation, State, State]] = [] |
| 100 | new_state = state |
| 101 | if not need_old_state and self.operations: |
| 102 | # Single working copy so state_forward doesn't mutate the original |
| 103 | new_state = state.clone() |
| 104 | for operation in self.operations: |
| 105 | if not getattr(operation, "reversible", True): |
| 106 | raise ValueError(f"Operation {operation} in {self} is not reversible") |
| 107 | if need_old_state: |
| 108 | new_state = new_state.clone() |
| 109 | old_state = new_state.clone() |
| 110 | operation.state_forward(self.app_label, new_state) |
| 111 | to_run.insert(0, (operation, old_state, new_state)) |
| 112 | else: |
| 113 | operation.state_forward(self.app_label, new_state) |
| 114 | |
| 115 | for operation, to_state, from_state in to_run: |
| 116 | if collect_sql and schema_editor: |
| 117 | schema_editor.collected_sql.append("--") |
| 118 | schema_editor.collected_sql.append(f"-- {operation.describe()}") |
| 119 | schema_editor.collected_sql.append("--") |
| 120 | if not operation.reduces_to_sql: |
| 121 | schema_editor.collected_sql.append( |
| 122 | "-- THIS OPERATION CANNOT BE REPRESENTED AS SQL" |
| 123 | ) |
| 124 | continue |
| 125 | before = len(schema_editor.collected_sql) |
| 126 | await self._run_database_backward( |
| 127 | operation, from_state, to_state, schema_editor, supports_transactions |
| 128 | ) |
| 129 | if len(schema_editor.collected_sql) == before: |
| 130 | schema_editor.collected_sql.append("-- (no-op)") |
| 131 | continue |
| 132 | if dry_run or not schema_editor: |
| 133 | continue |
| 134 | await self._run_database_backward( |
| 135 | operation, from_state, to_state, schema_editor, supports_transactions |
| 136 | ) |
| 137 | return state |
| 138 | |
| 139 | async def _run_database_forward( |
| 140 | self, |