Hooks allow executing custom code as part of processing a CRUD request. You can use this to validate data, call another custom API, place messages on queues and many other things.
Define a method, and register it with :class:`PiccoloCRUD <piccolo_api.crud.endpoints.PiccoloCRUD>`:
# app.py
from piccolo_api.crud.endpoints import PiccoloCRUD
from movies.tables import Movie
# set movie rating to 10 before saving
async def set_movie_rating_10(row: Movie):
row.rating = 10
return row
# set movie rating to 20 before saving
async def set_movie_rating_10(row: Movie):
row.rating = 20
return row
async def pre_delete(row_id):
pass
# Register one or multiple hooks
app = PiccoloCRUD(
table=Movie,
read_only=False,
hooks=[
Hook(hook_type=HookType.pre_save, callable=set_movie_rating_10),
Hook(hook_type=HookType.pre_save, callable=set_movie_rating_20),
Hook(hook_type=HookType.pre_delete, callable=pre_delete)
]
)You can specify multiple hooks (also per hook_type). Hooks are executed in order.
You can use either async or regular functions.
There are different hook types, and each type takes a slightly different set of inputs.
It's also important to return the expected data from your hook.
This hook runs during POST requests, prior to inserting data into the database.
It takes a single parameter, row, and should return the row:
async def set_movie_rating_10(row: Movie):
row.rating = 10
return row
app = PiccoloCRUD(
table=Movie,
read_only=False,
hooks=[
Hook(hook_type=HookType.pre_save, callable=set_movie_rating_10)
]
)This hook runs during POST requests, after inserting data into the database.
It takes a single parameter, row.
post_save hooks should not return data.
async def print_movie(row: Movie):
print(f'Movie {row.id} added to db.')
app = PiccoloCRUD(
table=Movie,
read_only=False,
hooks=[
Hook(hook_type=HookType.pre_save, callable=print_movie)
]
)This hook runs during PATCH requests, prior to changing the specified row in the database.
It takes two parameters, row_id which is the id of the row to be changed,
and values which is a dictionary of incoming values.
Each function must return a dictionary which represent the data to be modified.
async def reset_name(row_id: int, values: dict):
current_db_row = await Movie.objects().get(Movie.id==row_id)
if values.get("name"):
values["name"] = values["name"].replace(" ", "")
return values
app = PiccoloCRUD(
table=Movie,
read_only=False,
hooks=[
Hook(hook_type=HookType.pre_patch, callable=reset_name)
]
)This hook runs during PATCH requests, after changing the specified row in the database.
It takes two parameters, row_id which is the id of the row to be changed,
and values which is a dictionary of incoming values.
post_patch hooks should not return data.
async def print_movie_changes(row_id: int, values: dict):
current_db_row = await Movie.objects().get(Movie.id==row_id)
print(f'Movie {row_id} updated with values {values}')
app = PiccoloCRUD(
table=Movie,
read_only=False,
hooks=[
Hook(hook_type=HookType.post_patch, callable=print_movie_changes)
]
)This hook runs during DELETE requests, prior to deleting the specified row in the database.
It takes one parameter, row_id which is the id of the row to be deleted.
pre_delete hooks should not return data.
async def pre_delete(row_id: int):
pass
app = PiccoloCRUD(
table=Movie,
read_only=False,
hooks=[
Hook(hook_type=HookType.pre_delete, callable=pre_delete)
]
)This hook runs during DELETE requests, after deleting the specified row in the database.
It takes one parameter, row_id which is the id of the row to be deleted.
post_delete hooks should not return data.
async def post_delete(row_id: int):
pass
app = PiccoloCRUD(
table=Movie,
read_only=False,
hooks=[
Hook(hook_type=HookType.pre_delete, callable=post_delete)
]
)Each hook can optionally receive the Starlette request object. Just
add request as an argument in your hook, and it'll be injected automatically.
async def set_movie_rating_10(row: Movie, request: Request):
..... currentmodule:: piccolo_api.crud.hooks
.. autoclass:: HookType
:members:
:undoc-members:
.. autoclass:: Hook