Flask Web Development by Miguel Grinberg
By Jonathan Machado
April 8, 2021
Error handling: Keeping your code DRY
You might have seen projects where common scenarios are handled in every view, for example, getting a User by ID but returning an error if it hasn't been found.
It's common to see views like these handling the "user is not found" case:
# /rest/users.py
def get_user(user_id):
user = Users.get_or_none(Users.id == user_id))
if not user:
return jsonify({"error": "User not found"}), 404
return jsonify(model_to_dict(user))
# OR alternatively:
def get_user(user_id):
try:
user = Users.get(Users.id == user_id))
except Users.DoesNotExist:
return jsonify({"error": "User not found"}), 404
return jsonify(model_to_dict(user))
This scenario might repeat itself dozens of times throughout your codebase for similar endpoints.
However, we can create global error handlers to catch those exceptions. These handlers can be created on the app level, for all blueprints or for a specific blueprint:
# /rest/error_handlers.py
error_handler = Blueprint("error_handler", __name__)
@error_handler.app_errorhandler(DoesNotExist)
def does_not_exist(error):
# we could log the error to Sentry here, if we want.
return jsonify({"error": "Object not found"}), 404
app_errorhandler()
creates a handler for all blueprints. We could also use errorhandler()
instead which registers an error handler that becomes active for that blueprint only.
This allows our views to be fairly simple now!
# /rest/users.py
def get_user(user_id):
user = Users.get(Users.id == user_id))
return jsonify(model_to_dict(user))
Users.get
will raise a DoesNotExist
exception if no entry is found in the database, which is then handled by our does_not_exist()
global error handler.