feat: add restore and restore_by for soft-deletable models#7
Merged
Conversation
Implements restore/restore_by across v1 and v2 architectures: - v2 Repository: SoftDeleteMixin.restore() / .restore_by() Bypasses default soft-delete filters so deleted records can be restored. - v2 Service: SoftDeleteServiceMixin.restore() / .restore_by() Supports actor_id for updated_by tracking. - v1 Repository: SoftDeletableRepo.restore() / .restore_by() - v1 Service: SoftDeletableService.restore() / .restore_one_by() / .restore_all_raw_by() - Updated docs/v2 with usage examples - Added integration tests for all new methods
There was a problem hiding this comment.
Pull request overview
Adds “restore” capabilities to the existing soft-delete feature set, spanning both the v1 (Filter-based) and v2 (mixins/protocols + service mixins) architectures, plus docs and integration tests to validate the new behavior.
Changes:
- Add
restore/restore_byto v2 repositories (SoftDeleteMixin) and services (SoftDeleteServiceMixin), includingactor_idsupport forupdated_by. - Add
restore/restore_byto v1SoftDeletableRepoand corresponding service helpers. - Update v2 docs with restore usage examples and add integration tests covering restore flows.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/v2/test_integration/test_service_crud.py | Adds service-level integration tests for restore flows (pk, by filters, raw, actor_id). |
| tests/v2/test_integration/test_repository_crud.py | Adds repository-level integration tests for restore flows (pk, by filters, extra payload). |
| tests/v1/test_integration/test_base_repo.py | Adds integration tests for v1 repo restore behavior. |
| src/notora/v2/services/mixins/delete.pyi | Extends v2 service mixin typing surface with restore* overloads. |
| src/notora/v2/services/mixins/delete.py | Implements restore* methods in SoftDeleteServiceMixin. |
| src/notora/v2/repositories/mixins/write.py | Implements v2 repo restore / restore_by statement builders. |
| src/notora/v2/repositories/base.py | Extends SoftDeleteRepositoryProtocol with restore* methods. |
| src/notora/v1/services/base.py | Adds v1 SoftDeletableService.restore* helpers. |
| src/notora/v1/persistence/repos/base.py | Implements v1 SoftDeletableRepo.restore* update statements. |
| docs/v2/services.md | Documents service-level restore usage. |
| docs/v2/repositories.md | Documents repository-level restore usage and intent. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -381,7 +381,116 @@ async def test_service_soft_delete_by_returns_list( | |||
|
|
|||
| assert len(deleted) == len(payloads) | |||
| assert all(isinstance(d, V2UserResponseSchema) for d in deleted) | |||
Comment on lines
+188
to
+192
| payload.update(additional_payload) | ||
| stmt = update(self.model).values(**payload) | ||
| stmt = self.apply_filters(stmt, filters, apply_default_filters=False) | ||
| stmt = stmt.returning(self.model) | ||
| return self.apply_options(stmt, options) |
Comment on lines
+340
to
+345
| def restore_by( | ||
| self, | ||
| filters: Iterable[Filters] = (), | ||
| ) -> TypedReturnsRows[tuple[ModelType]]: | ||
| query = update(self.model).values({'deleted_at': None}) | ||
| for predicate in self._get_query_predicates(filters): |
Comment on lines
+168
to
+177
| `restore` and `restore_by` clear `deleted_at` back to `NULL`. They bypass the | ||
| soft-delete default filter so they can target already-deleted records: | ||
|
|
||
| ```python | ||
| # Restore by primary key | ||
| query = repo.restore(user_id) | ||
|
|
||
| # Restore by filters | ||
| query = repo.restore_by( | ||
| filters=[lambda m: m.email == "a@b.com"], |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements
restoreandrestore_bymethods for soft-deletable models across v1 and v2 architectures.Changes
SoftDeleteMixin.restore()/.restore_by()SoftDeleteServiceMixin.restore()/.restore_by()actor_idforupdated_bytrackingSoftDeletableRepo.restore()/.restore_by()SoftDeletableService.restore()/.restore_one_by()/.restore_all_raw_by()docs/v2/repositories.mdanddocs/v2/services.mdwith usage examplesTest results
All 346 tests pass, linters clean (ruff, mypy, pyright, stubtest).