Let's say I have two entities, Users
and Councils
, and a M2M association table UserCouncils
. Users
can be added/removed from Councils
and only admins can do that (defined in a role
attribute in the UserCouncil
relation). Now, when creating endpoints for /councils/{council_id}/remove
, I am faced with the issue of checking multiple constraints before the operation, such as the following:
@router.delete("/{council_id}/remove", response_model=responses.CouncilDetail)
def remove_user_from_council(
council_id: int | UUID = Path(...),
*,
user_in: schemas.CouncilUser,
db: Session = Depends(get_db),
current_user: Users = Depends(get_current_user),
council: Councils = Depends(council_id_dep),
) -> dict[str, Any]:
"""
DELETE /councils/:id/remove (auth)
remove user with `user_in` from council
current user must be ADMIN of council
"""
# check if input user exists
if not Users.get(db=db, id=user_in.user_id):
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="User not found"
)
if not UserCouncil.get(db=db, user_id=user_in.user_id, council_id=council.id):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Cannot delete user who is not part of council",
)
# check if current user exists in council
if not (
relation := UserCouncil.get(
db=db, user_id=current_user.id, council_id=council.id
)
):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Current user not part of council",
)
# check if current user is Admin
if relation.role != Roles.ADMIN:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN, detail="Unauthorized"
)
elif current_user.id == user_in.user_id:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Admin cannot delete themselves",
)
else:
updated_users = council.remove_member(db=db, user_id=user_in.user_id)
result = {"council": council, "users": updated_users}
return result
These checks are pretty self-explanatory. However, this adds a lot of code in the endpoint definition. Should the endpoint definitions be generally minimalistic? I could wrap all these checks inside the Councils
crud method (i.e., council.remove_member()
), but that would mean adding HTTPException
s inside crud classes, which I don't want to do.
What are the general best practices for solving situations like these, and where can I read more about this? Any kind of help would be appreciated.
Thanks.
Aucun commentaire:
Enregistrer un commentaire