I am not sure how can I catch an Exception in my django app.
I have a view, where I use my own class object which uses Requests framework to make remote API requests.
I need to catch Timeout exception inside my class code. When Timeout exception appears I need to return custom error page. My class code:
try:
response = requests.request(
method=method, url=self.url, auth=self.auth, timeout=3, verify=False, **kwargs
)
except requests.Timeout:
return HttpResponse('API connection timeout')
return response
However, in my django view code, I use something like:
user_details = auth_client['auth/users/%s/details' % (request.POST.get('username'))].get().json()['result']
so, when I try to return HttpResponse from class code, I get error:
'HttpResponse' object has no attribute 'json'
I don't know how can I "omit" code from view and show HttpResponse directly. Any suggestions?
You shouldn't be trying to return an HttpResponse in your requests class. Instead of catching the exception and replacing it with an unwanted HttpResponse, let it propagate and catch it in the view:
try:
user_details = auth_client['auth/users/%s/details' % (request.POST.get('username'))].get().json()['result']
except requests.Timeout:
return HttpResponse('whatever')
Edit OK, so here's another solution. You say that this is a class: so you can define a method, say get_user_details, which either returns the actual JSON, or none.
def get_user_details(username):
try:
user_details = self['auth/users/%s/details' % 'username'].get()
except requests.Timeout:
return []
return user_details.json()['result']
so now in your view you just call auth_client.get_user_details(request.POST['username']).
Related
Using the Chalice BadRequestResponse class for view exception handling is causing an exception stating the BadRequestResponse is not JSON serializable. Why is Chalice trying to cast the view output as JSON when the BadRequestResponse is returned from the view?
#auth.route('/auth/register', methods=['POST'])
def login():
user_data = auth.current_request.json_body
try:
UserSchema().load(user_data)
user = User(**user_data)
user.save()
except ValidationError as e:
return BadRequestError("Why! This shouldn't be serialized to JSON!")
else:
return Response(status_code=201, body=user_data)
Whoops! I'm trying to return instead of raising the BadRequestError which is causing Chalice to cast the exception to JSON.
I am using django class based view and rest framework
object = self.get_object()
In Detail view if object does not exist and i do get request like
/user/10
then i get this response
{"detail": "not found"}
Now i want to customize that response
like
try:
obj = self.get_object()
except:
raise Exception("This object does not exist")
But thats not working
We can implement a custom exception handler function that returns the custom response in case the object does not exist.
In case a object does not exist, Http404 exception is raised. So, we will check if the exception raised is Http404 and if that is the case, we will return our custom exception message in the response.
from rest_framework.views import exception_handler
from django.http import Http404
def custom_exception_handler(exc, context):
# Call REST framework's default exception handler first,
# to get the standard error response.
response = exception_handler(exc, context)
if isinstance(exc, Http404):
custom_response_data = {
'detail': 'This object does not exist.' # custom exception message
}
response.data = custom_response_data # set the custom response data on response object
return response
After defining our custom exception handler, we need to add this custom exception handler to our DRF settings.
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'
}
You could create a custom exception class as below, and it would raise APIException with a custom message and custom status_code
from rest_framework.serializers import ValidationError
from rest_framework import status
class CustomAPIException(ValidationError):
"""
raises API exceptions with custom messages and custom status codes
"""
status_code = status.HTTP_400_BAD_REQUEST
default_code = 'error'
def __init__(self, detail, status_code=None):
self.detail = detail
if status_code is not None:
self.status_code = status_code
and in your views,
from rest_framework import status
try:
obj = self.get_object()
except:
raise CustomAPIException("This object does not exist", status_code=status.HTTP_404_NOT_FOUND)
The response will be like this
{"detail": "This object does not exist"}
NOTE
the detail parameter of CustomAPIException class takes str,list and dict objects. If you provide a dict object, then it will return that dict as exception response
UPDATE
As #pratibha mentioned, it's not possible to produce desired output if we use this exception class in Serializer's validate() or validate_xxfieldName() methods.
Why this behaviour ?
I wrote a similar answer in SO, here Django REST Framework ValidationError always returns 400
How to obtain desired output within the serializer's validate() method?
Inherit CustomAPIException from rest_framework.exceptions.APIException instead of from rest_framework.serializers.ValidationError
ie,
from rest_framework.exceptions import APIException
class CustomAPIException(APIException):
# .... code
For a web app, I have a method get_stuff_for_payment that raises PermissionDenied for which Django generates a HTTP 403 to the user.
I am now asked to show a friendly error page instead of the generic 403. The following code does it. But how do I retain the stacktrace when I do this?
try:
stuff = get_stuff_for_payment(id)
except PermissionDenied:
return render(request, 'friendly_error_page.html', context)
If you want just to log it, then do so. The logging.exception method will log a message and append a traceback automatically:
except PermissionDenied as err:
logger.exception('Permission was denied: %s', err)
StackTrace of Errors on our custom Template in python
except PermissionDenied:
return render(request, 'friendly_error_page.html', context)
is not a good way in django gor dealing with 403 , 4xx or 5xx errors.
If we want to show exceptions which are generated , on ur template(403.html) then we could write your own 403 view, grabbing the exception and passing it to your 403 template.
Steps:
#.In views.py:
import sys,traceback
def custom_403(request):
t = loader.get_template('403.html')
logger.info("custom msg",sys.exc_info()) // for traceback
type, value, tb = sys.exc_info()
return HttpResponseServerError(t.render(Context({
'exception_value': value,
'value':type,
'tb':traceback.format_exception(type, value,
tb)
},RequestContext(request))))
#.In Main Urls.py:
from django.conf.urls.defaults import *
handler403 = 'Project.web.services.views.custom_403'
and if you want to show stacktrace to user use this template otherwise your custom template
#.In Template(403.html):
{{ exception_value }}{{value}}{{tb}}
I’m trying to create a custom error response from the REST Django framework.
I’ve included the following in my views.py,
from rest_framework.views import exception_handler
def custom_exception_handler(exc):
"""
Custom exception handler for Django Rest Framework that adds
the `status_code` to the response and renames the `detail` key to `error`.
"""
response = exception_handler(exc)
if response is not None:
response.data['status_code'] = response.status_code
response.data['error'] = response.data['detail']
response.data['detail'] = "CUSTOM ERROR"
return response
And also added the following to settings.py.
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.AllowAny',
),
'EXCEPTION_HANDLER': 'project.input.utils.custom_exception_handler'
}
Am I missing something as I don’t get the expected response. i.e a custom error message in the 400 API response.
Thanks,
As Bibhas said, with custom exception handlers, you only can return own defined errors when an exception is called. If you want to return a custom response error without exceptions being triggered, you need to return it in the view itself. For example:
return Response({'detail' : "Invalid arguments", 'args' : ['arg1', 'arg2']},
status = status.HTTP_400_BAD_REQUEST)
From the documentation -
Note that the exception handler will only be called for responses generated by raised exceptions. It will not be used for any responses returned directly by the view, such as the HTTP_400_BAD_REQUEST responses that are returned by the generic views when serializer validation fails.
I have got JSON API (as the part of the project) implemented with Django.
Sometimes I return error to user in JSON form. I want to notify admin via standard error reporting procedure via email (for example when uncaught exception raised). But I want to return some JSON response too, not 500 error page.
Some metacode to make sure that everything clear:
def request_handler(request):
try:
code_that_rises_an_exception()
return response('{"result":"success"}')
except Exception,e:
notify_admin_about_error(e) # <- this function is required
return response('{"result":"error"}')
Thank you!
You can use Django Middleware for this. Middlewares allow you to modify/process Django's HttpRequest object that is supplied to a view and HttpResponse object that is returned by a view as well as take action when a view raises an exception. You can do variety of tasks with this such as logging of the meta data of the requests you receive, error reporting etc. Django calls process_exception whenever an exception is raised by a view and you can define process_exception() to send a mail to you whenever an exception gets raised.
class ErrorReportingMiddleware(object):
def process_exception(self, request, exception):
send_mail_to_admin(exception) # you can collect some more information here
return HttpResponse('{"result":"error"}') # this response is returned to the user
Add this class to your MIDDLEWARE_CLASSES variable in settings.py at the end of that tuple.
Your view would reduce to:
def request_handler(request):
code_that_rises_an_exception()
return response('{"result":"success"}')
Now if an exception is raised by request_handler, Django would call process_exception method of your ErrorReportingMiddleware which will send a mail to the admin about the exception and return a JSON response to the browser instead of a 500 page. I would implement send_mail_to_admin as an asynchronous function so that the processing of the response by django doesn't get blocked due to sending of mail and quick response is returned to the user.