How to use exception handling in Django view - django

Suppose i have this code
if form.is_valid():
form.save()
Now suppose my form is valid i have exception that foregin key value is linked to more than one column so it will raise exception
Now i want to know is there any way to grab that exception value and pass to jquery via AJAX
Because form is valid so it comes inside the loop but its not able to go after form.save
So how can i program that if exception occurs it get passed to jquery like
if exception
return HttpResponse(exception)
I get this exception
MultipleObjectsReturned at
/manage/Source/create/ get() returned
more than one Account -- it returned
3! Lookup parameters were
{'account_number':
u'121121'}
What type of exception it is

MultipleObjectsReturned is the exception.
try:
#do something
except MultipleObjectsReturned:
return HttpResponse('MultipleObjectsReturned')
I wouldn't recommend using a bare try/except to catch all exceptions, as you won't know exactly what's wrong.

Related

Flask API: what's the best way to handle exceptions?

I'm building a very simple "to-do" API which only has 3 files: a controller with all the endpoints, a service with some logic to manipulate data and a DAO for DB access. So whenever an endpoint gets a request, it calls a method from the service and this method calls a DAO method to interact with the DB.
For example, the service has an add_item method which converts a field ("status") into an enum value, and a KeyError occurs when the value is something else. So my method goes something like this (self.dao is an instance of the DAO class and ValidStatus is the enum):
def add_item(self, name: str, status: str) -> int:
valid_status = ValidStatus[status.upper()]
inserted_id = self.dao.insert(item_name=name, item_status=valid_status)
return inserted_id
Since I'm now trying to handle exceptions, I modified the method to add a try-except block:
def add_item(self, name: str, status: str) -> Union[int, str]:
try:
valid_status = ValidStatus[status.upper()]
inserted_id = self.dao.insert_task(item_name=name, item_status=valid_status)
return inserted_id
except KeyError as e:
return (f"{e} status is not valid. Please enter {ValidStatus.TODO} or {ValidStatus.COMPLETED}.")
But I'm not sure this is a good approach, since now the method returns 2 different value types: the id of the new item if everthing is ok, or a string with the error message if the exception occurs. And I'm not sure that's the best choice, consistency-wise (and because all my type hinting would get messed up with Unions).
I thought another option would be to always return a tuple with a dict to create a json formatted response and an http code. So when everything goes well it would be something like return {"new item id": inserted_id}, 200 and when there's an exception it would be something like
return {"error": "Something went wrong"}, 400. But I'm not sure this is a good approach either, since then the service would be dealing with http status codes and json structured dicts, and that sounds more like a controller's responsibility.
So is there an advised best practice for this?
Well, generally speaking, there are two types of errors to handle, one is user input-related errors, such as the status key error in your case, which usually return 4xx, and the other is server-side processing errors, such as database errors, which usually return 5xx.
For the first type of error, we often use some kind of validator, such as marshmallow, and handle those errors in the controller. For example,
#bp.route("", methods=["POST"])
#login_required
def new_paper():
req = request.json
try:
cleaned_data = CreateUpdatePaperSchema(unknown=EXCLUDE).load(req)
except ValidationError as err:
return err_response(err_msg=fmt_validate_err(err), status_code=400)
For the second type of error, we often use a global exception catching mechanism to handle that uniformly. For example,
#app.errorhandler(Exception)
def handle_exception(exc):
db.session.rollback()
app.logger.error(exc, exc_info=True)
err_msg = "Something went wrong"
return err_response(err_msg=err_msg, status_code=500)

How to handle "matching query does not exist" when getting an object

When I want to select objects with a get() function like
personalProfile = World.objects.get(ID=personID)
If get function doesn't return find a value, a "matching query does not exist." error occurs.
If I don't need this error, I'll use try and except function
try:
personalProfile = World.objects.get(ID=personID)
except:
pass
But I think this is not the best way since I use
except:
pass
Please recommend some idea or code sample to fight with this issue
That depends on what you want to do if it doesn't exist..
Theres get_object_or_404:
Calls get() on a given model manager, but it raises Http404 instead of the model’s DoesNotExist exception.
get_object_or_404(World, ID=personID)
Which is very close to the try except code you currently do.
Otherwise theres get_or_create:
personalProfile, created = World.objects.get_or_create(ID=personID)
Although, If you choose to continue with your current approach, at least make sure the except is localised to the correct error and then do something with that as necessary
try:
personalProfile = World.objects.get(ID=personID)
except MyModel.DoesNotExist:
raise Http404("No MyModel matches the given query.")
The above try/except handle is similar to what is found in the docs for get_object_or_404...
A get_or_none() function has been proposed, multiple times now. The rejection notice is feature creep, which you might or might not agree with. The functionality is present --with slightly different semantics-- in the first() queryset method.
But first things first:
The manager throws World.DoesNotExist, a specialized subclass of ObjectDoesNotExist when a World object was not found:
try:
personalProfile = World.objects.get(ID=personID)
except World.DoesNotExist:
pass
There's also get_object_or_404() which raises a Http404 exception when the object was not found.
You can also roll your own get_or_none(). A possible implementation could be:
def get_or_none(queryset, *args, **kwargs):
try:
return queryset.get(*args, **kwargs)
except ObjectDoesNotExist:
return None
Note that this still raises MultipleObjectsReturned when more than one matching object is found. If you always want the first object regardless of any others, you can simplify using first(), which returns None when the queryset is empty:
def get_or_none(queryset, *args, **kwargs):
return queryset.filter(*args, **kwargs).first()
Note however, for this to work reliably, you need a proper order for objects, because in the presence of multiple objects first() might be non-deterministic (it probably returns the first object from the database index used to filter the query and neither indexes not the underlying tables need be sorted or even have a repeatable order).
Use both, however, only when the use of the object to retrieve is strictly optional for the further program flow. When failure to retrieve an object is an error, use get_object_or_404(). When an object should be created when it does not exist, use get_or_create(). In those cases, both are better suited to simplify program flow.
As alasdair mentioned you could use the built in first() method.
It returns the object if it exists or None if it's not
personalProfile = World.objects.filter(ID=personID).first()

Improve upon the try-catch python method

I am basically trying to improve upon Python: try-except as an Expression? with a couple of features
Ability to pass in extra args and kwargs to the "try-except" question. This is because I have a success function which takes in a few parameters
Failure function if callable should be called with the exception argument so it gets a chance to handle it.
Here is a sample code with a test however I am not able to get the last line to work
def method2(exc_class = None):
if exc_class:
raise exc_class()
def method1():
return "Hello world"
def try_except(function, failure, exceptions = [], args = [], kwargs = {}):
"""
Run the given function with args and kwargs. If it throws one of the
exceptions in the list then either return failure or call failure function
"""
try:
return function(*args, **kwargs)
except exceptions or Exception as e:
return failure(e) if callable(failure) else failure
if __name__ == "__main__":
#Prints hello world
print try_except(method1, "Failure")
#Prints Failure great!!
print try_except(method2, "Failure", kwargs = {"exc_class" : ValueError})
# I expect below line to print "Failure" properly but it throws a ValueError
print try_except(method2, "Failure", kwargs = {"exc_class" : ValueError}, exceptions=[ValueError])
My question here is the except exceptions or Exception as e: line does not substitute the list of exceptions properly. I cannot do *exceptions as shown in the original question because I want to take in extra parameters for the function.
I am okay to change the try_except to some extent though.
Note: I have considered doing a catch all except Exception as e and then checking if the exception class in in the list and if it is not then rethrow it. However that won't work because when I rethrow the original stack trace is lost and I do not want to do that.
When catching several exception types with one except clause, you can't use just any iterable object; you must specifically use a tuple. You can still allow try_except to take any iterable as an argument by creating a tuple in the except clause itself.
try:
return function(*args, **kwargs)
except tuple(exceptions or (Exception,)) as e:
return failure(e) if callable(failure) else failure
From the docs:
For an except clause with an expression, that expression is evaluated, and the clause matches the exception if the resulting object is “compatible” with the exception. An object is compatible with an exception if it is the class or a base class of the exception object, or a tuple containing an item compatible with the exception.

Should unrecoverable exceptions from HTTP parameter problems be caught?

Is it necessary to catch errors stemming from HTTP inputs? Is it ever a good idea to let it fail naturally (allow the exception to bubble up)?
I have a Django view for a server side interface for an AJAX call that looks something like this:
def some_view(request):
try:
some_int = int(request.POST.get('some_int')) # May raise ValueError or TypeError
except (ValueError, TypeError):
return HttpResponseBadRequest('some_int must be an int')
# ... Code that assumes some_int is an int
return HttpResponse('The normal response')
Is it ever acceptable in production code to have something like this?
def some_view(request):
some_int = int(request.POST.get('some_int')) # Ignore ValueError or TypeError raised
# ... Code that assumes some_int is an int
return HttpResponse('normal_response')
As I accept more parameters, I find that it is frustrating to maintain so many try/except blocks which are mostly the same and I end up with a ton of boiler plate code.
Of course I tried to refactor this into a separate function but since Django requires an HttpResponse to be returned, not raised as an exception, I can't plug it into a view without a try/except block. Also, conversions to int aren't the only thing I check... there are a lot of business logic sanity checks performed depending on the input as well. For example, I would validate that the JSON passed is of a specific format (i.e. array of objects of int array, etc.).
My views end up being 70+ lines of code just for sanity checks and a few lines of code that actually generate the response. Somehow I feel like there should be a more elegant way but I haven't found one so I'm considering forgoing all checks and just letting Django take care of it. Is this a bad idea?
I'm aware of the following potential problems if I don't catch the exceptions:
The same HTTP 500 is returned for all errors
If logging is enabled in production, it would probably log an error every time an invalid input occurs
Are there other problems I should be aware of? It just feels wrong not to catch exceptions from user inputs even though there's not much I can do about it in terms of recovery logic.
I think the best way to handle this is by writing your own middleware that catches the exceptions and turns them into your desired response.
That might look something like this:
# views.py
def some_view(request):
some_int = int(request.POST.get('some_int'))
# ... Code that assumes some_int is an int
return HttpResponse('normal_response')
# middleware.py
class MyValidationMiddleware(object):
def process_exception(self, request, e):
if isinstance(e, ValueError):
return HttpResponseBadRequest('Input did not validate')
else:
return None # let it bubble up
(Since middleware is site-wide, you might want to explicitly define your own input-validation exceptions to distinguish them from other sorts of exceptions that might occur.)
Alternatively, you could do the same sort of thing with per-view decorators.

Django ValidationError - how to use this properly?

Currently there is code that is doing (from with the form):
# the exception that gets raised if the form is not valid
raise forms.ValidationError("there was an error");
# here is where form.is_valid is called
form.is_valid() == False:
response['msg']=str(form.errors)
response['err']='row not updated.'
json = simplejson.dumps( response ) #this json will get returned from the view.
The problem with this, is that it is sending err message to the client as:
__all__"There was an error."
I want to remove the "all" garbage from the error template that is returned. How can I go about doing this? it seems to get added deep in django form code.
It's because the error is not associated with any field in particular, but it's so called non-field error.
If you're only interested in non-field errors, just simply pass this to the response:
response['msg']=str(form.errors['__all__'])
errors is an instance of a subclass of dict with some special rendering code. Most of the keys are the fields of the form, but as the docs describe, raising ValidationError in clean produces an error message that isn't associated with any particular field:
Note that any errors raised by your Form.clean() override will not be associated with any field in particular. They go into a special “field” (called __all__), which you can access via the non_field_errors() method if you need to. If you want to attach errors to a specific field in the form, you will need to access the _errors attribute on the form, which is described later.
https://docs.djangoproject.com/en/dev/ref/forms/validation/
So you can either generate your string representation of the errors differently (probably starting with form.errors.values() or form.errors.itervalues(), and maybe using the as_text method of the default ErrorList class) or associate your error with a particular field of the form as described in the docs:
When you really do need to attach the error to a particular field, you should store (or amend) a key in the Form._errors attribute. This attribute is an instance of a django.forms.utils.ErrorDict class. Essentially, though, it’s just a dictionary. There is a key in the dictionary for each field in the form that has an error. Each value in the dictionary is a django.forms.utils.ErrorList instance, which is a list that knows how to display itself in different ways. So you can treat _errors as a dictionary mapping field names to lists.
If you want to add a new error to a particular field, you should check whether the key already exists in self._errors or not. If not, create a new entry for the given key, holding an empty ErrorList instance. In either case, you can then append your error message to the list for the field name in question and it will be displayed when the form is displayed.
https://docs.djangoproject.com/en/dev/ref/forms/validation/#form-subclasses-and-modifying-field-errors