A lot of my Django views start a little bit like this:
try:
# here request.POST could also be request.GET or a captured URL parameter
MyModel.objects.get(user = request.user, some_attr = int(request.POST['some_val']))
except KeyError:
# error_view is a view that returns a pretty, formatted 500 response
return error_view(request, message = 'Incomplete data was submitted (some_val was missing). No action was taken.', next = request.get_full_path())
except ValueError:
return error_view(request, message = 'The data (some_val = %s) was not formatted correctly. No action was taken.' % request.POST['some_val'], next = request.get_full_path())
except MyModel.DoesNotExist:
return error_view(request, message = 'The data you were looking for (some_val = %s) could not be found. It is possible you used an outdated form, or the data was just recently removed. No action was taken.' % request.POST['some_val'], next = request.get_full_path())
I find myself repeating code very similar to this a lot (but with different POST/GET/url, filter arguments, data type, next url or messages), sometimes several for one view.
What is the best way to approach this?
- Just keep repeating these 8 lines, the code is different enough to not violate DRY
- Make a decorator which passes the instance to the view, or returns the error view. Changes the function signature (is that ok?) and not that easy to generalize.
- Write a function to do this. In that case, should it...
- ... return either a response or an instance, return it if it's a response.
- ... return a (instance, response) tuple, oneo f which is None, retuning response if it's not None.
- ... raise a special type of exception (say VisibleException), and create a middleware to display it using
error_view
.
- ... something else?
All the methods work but which one do you recommend and why?
I came up with 3.3 just now and it actually makes sense to me - these situations are abnormal exit routes after all - but I don't think I've seen it used before (admittedly I don't know what to search for), so perhaps it has a problem. (The programmer using the app has to add a Middleware, that's one).
EDIT: I was just reminded that get_object_or_404
throws an exception that is kind of displayed to the user, so perhaps 3.3 is a logical extension.
int(request.POST['some_val'])
by hand, use a form with asome_val = IntegerField(required=True)
in it. Use the core tools of Django.