1

According to what I've read EAFP (Easier to Ask for Forgiveness than Permission) is an accepted coding style in Python, actually recommended in many cases.

My question is about when that becomes too much with custom exceptions. Imagine we have a web application for a dog hotel with the following structure:

- controllers
    -- dogs.py (Receives REST requests)
- services
    -- dogs.py (Connects to a DB and a third party API to retrieve dog breed information)
    -- validator.py

Now three requests examples:

  1. Someone wants to create a new dog, but gives invalid information. So the validator has a is_valid_dog method that returns False.
  2. Someone makes a GET request to retrieve information about a certain dog that does not exist. The dogs service hits the database but nothing is found.
  3. The user wants dog breed information, the service hits the third party API but it's down (500 status obtained).

What is the best way to handle that type of flow?

  1. In the first case, should I raise an exception when the is_valid_dog returns a False? or return None/False to the controller so it can map it to the 400 status code?
  2. Should I return None so the controller maps the return value to 404?
  3. Should I catch the exception and raise a custom ThirdPartyAPIException with the message and the controller must know how to handle that?

I'm curious about when to return specific values or just raise exceptions across different layers so the controller can return the final status. And in general, where is the best place to handle those exceptions/flows.

2 Answers 2

1

You should follow DRF's approach, it's a the facto standard for RESTful django apis. Take a look at DRF's source code: https://github.com/encode/django-rest-framework/blob/master/rest_framework/exceptions.py.

  1. Create a default ValidationError exception, customizing each error depending on your context. If the exception is raised, return a 400 status code;

  2. If None is returned, raise a NotFound exception, with a 404 status code;

  3. I would stick with catching the exception and displaying a 503 code, "Service Unavailable".

Take a look at class APIException to understand the flow of exceptions and response of these errors.

Sign up to request clarification or add additional context in comments.

Comments

0

For the first request example:

  • I would return the error in valid_dog to the controller so it can be shown in response which will raise a 400 Bad Request status code.

For the second request example:

  • I would return the empty query to the controller which will raise a 404 Not Found status code.

For the third request example:

  • I would handle the exception in the controller by raising a 424 FAILED DEPENDENCY status code or 503 SERVICE UNAVAILABLE with response explaining the issue.

for example setting up custom exception for django rest framework:

from django.utils.encoding import force_text
from rest_framework.exceptions import APIException

class ServiceUnavailable(APIException):
    """
    Raise error for external services that cannot be reached
    """
    status_code = 503
    default_detail = "Service temporarily unavailable, try again later."
    default_code = "service_unavailable"

    def __init__(self, detail, field):
        if detail is not None:
            self.detail = {field: [force_text(detail)]}
        else:
            self.detail = {"detail": force_text(self.default_detail)}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.