add on delete hook (#1014)

Co-authored-by: Schwannden Kuo <schwannden@mobagel.com>
This commit is contained in:
schwannden
2022-06-20 19:26:02 +08:00
committed by GitHub
parent 0efbab42cd
commit 3bb3728261
4 changed files with 90 additions and 1 deletions

View File

@ -263,3 +263,50 @@ class UserManager(UUIDIDMixin, BaseUserManager[User, uuid.UUID]):
async def on_after_reset_password(self, user: User, request: Optional[Request] = None):
print(f"User {user.id} has reset their password.")
```
#### `on_before_delete`
Perform logic before user delete.
For example, you may want to **valide user resource integrity** to see if any related user resource need to be marked inactive, or delete
them recursively.
**Arguments**
* `user` (`User`): the user to be deleted.
* `request` (`Optional[Request]`): optional FastAPI request object that triggered the operation. Defaults to None.
**Example**
```py
from fastapi_users import BaseUserManager, UUIDIDMixin
class UserManager(UUIDIDMixin, BaseUserManager[User, uuid.UUID]):
# ...
async def on_before_delete(self, user: User, request: Optional[Request] = None):
print(f"User {user.id} is going to be deleted")
```
#### `on_after_delete`
Perform logic after user delete.
For example, you may want to **send an email** to the administrator about the event.
**Arguments**
* `user` (`User`): the user to be deleted.
* `request` (`Optional[Request]`): optional FastAPI request object that triggered the operation. Defaults to None.
**Example**
```py
from fastapi_users import BaseUserManager, UUIDIDMixin
class UserManager(UUIDIDMixin, BaseUserManager[User, uuid.UUID]):
# ...
async def on_after_delete(self, user: User, request: Optional[Request] = None):
print(f"User {user.id} is successfully deleted")
```

View File

@ -403,13 +403,19 @@ class BaseUserManager(Generic[models.UP, models.ID]):
await self.on_after_update(updated_user, updated_user_data, request)
return updated_user
async def delete(self, user: models.UP) -> None:
async def delete(
self,
user: models.UP,
request: Optional[Request] = None,
) -> None:
"""
Delete a user.
:param user: The user to delete.
"""
await self.on_before_delete(user, request)
await self.user_db.delete(user)
await self.on_after_delete(user, request)
async def validate_password(
self, password: str, user: Union[schemas.UC, models.UP]
@ -516,6 +522,34 @@ class BaseUserManager(Generic[models.UP, models.ID]):
"""
return # pragma: no cover
async def on_before_delete(
self, user: models.UP, request: Optional[Request] = None
) -> None:
"""
Perform logic before user delete.
*You should overload this method to add your own logic.*
:param user: The user to be deleted
:param request: Optional FastAPI request that
triggered the operation, defaults to None.
"""
return # pragma: no cover
async def on_after_delete(
self, user: models.UP, request: Optional[Request] = None
) -> None:
"""
Perform logic before user delete.
*You should overload this method to add your own logic.*
:param user: The user to be deleted
:param request: Optional FastAPI request that
triggered the operation, defaults to None.
"""
return # pragma: no cover
async def authenticate(
self, credentials: OAuth2PasswordRequestForm
) -> Optional[models.UP]:

View File

@ -120,6 +120,8 @@ class UserManagerMock(BaseTestUserManager[models.UP]):
on_after_forgot_password: MagicMock
on_after_reset_password: MagicMock
on_after_update: MagicMock
on_before_delete: MagicMock
on_after_delete: MagicMock
_update: MagicMock
@ -475,6 +477,8 @@ def make_user_manager(mocker: MockerFixture):
mocker.spy(user_manager, "on_after_forgot_password")
mocker.spy(user_manager, "on_after_reset_password")
mocker.spy(user_manager, "on_after_update")
mocker.spy(user_manager, "on_before_delete")
mocker.spy(user_manager, "on_after_delete")
mocker.spy(user_manager, "_update")
return user_manager

View File

@ -532,6 +532,10 @@ class TestDelete:
):
await user_manager.delete(user)
assert user_manager.on_before_delete.called is True
assert user_manager.on_after_delete.called is True
@pytest.mark.asyncio
@pytest.mark.manager