views:

34

answers:

2

I've written a Django application which interacts with a third-party API (Disqus, although this detail is unimportant) via a Python wrapper. When the service is unavailable, the Python wrapper raises an exception.

The best way for the application to handle such exceptions is to suppress them so that the rest of the page's content can still be displayed to the user. The following works fine.

try:
    somemodule.method_that_may_raise_exception(args)
except somemodule.APIError:
    pass

Certain views contain several such calls. Is wrapping each call in try/except the best way to suppress possible exceptions?

+1  A: 

Certain views contain several such calls. Is wrapping each call in try/except the best way to suppress possible exceptions?

You can wrap the API call inside another function. Say:

def make_api_call(*args, **kwargs):
    try:
        return somemodule.method_that_may_raise_exception(*args, **kwargs)
    except somemodule.APIError:
        log.warn("....")           

This function can be called instead of the try/except block in each view. This will at least serve to reduce the number of lines of code you write and provide a common place for handling such exceptions.

Update

@Yorirou is correct. Changing code to add this good practice.

Manoj Govindan
Thanks, Manoj. By encapsulating the logic surrounding the API calls this approach could reduce the spaghetti-like nature of the view in question. :)
davidchambers
A: 

Making API calls from views is not so good idea. You should probably create another module, that does the job.

ie. when I make Facebook apps I create publish.py file to store all "publish to live stream" calls. Functions in that module are named based on when they should be called. Ie.:

# publish.py
def authorise_application(user):
    # API call "User joined app."

def post_anwser(anwser):
    # API call "User posted anwser to quiz".

Then your views are very clean:

# views.py
def post_anwser(request):
    ...
    if form.is_valid():
        form.save()
        publish.post_anwser(form.instance)

When you have your code organised that way, you can create decorator for ignoring exceptions:

# publish.py
def ignore_api_error(fun):
    def res(*args, **kwargs):
        try:
            return fun(*args, **kwargs):
        except someservice.ApiError:
            return None
    return res

@ignore_api_error
def authorised_application(user):
    # API call "User joined app."

@ignore_api_error
def posted_anwser(user, anwser):
    # API call "User posted anwser to quiz".

Also you can create function, that is not "ignored" by default, and add ignore code in view:

# publish.py
def some_function(user, message):
    pass

# views.py
def my_view():
    ...
    publish.ignore_api_error(publish.some_function)(user, message)
    ...
Tomasz Wysocki
I have a quibble regarding the `ignore_api_error` decorator. The OP says that the rest of the view should render cleanly even if the API call fails (_suppress them so that the rest of the page's content can still be displayed to the user_). Given this decorator an API exception would cause the view to return `None` which is not what the OP wants.
Manoj Govindan
This is not decorator for views, but for API functions. Please read my post again (I have added comments to be more clear).
Tomasz Wysocki
@Tomasz: Got it. I didn't understand it correctly the first time.
Manoj Govindan