I am creating a custom HTTP 500 error template. Why is it Django shows it when i raise an exception and not when I return HttpResponseServerError (I just get the default browser 500 error)? I find this behaviour strange...
The HttpResponseServerError inherits from HttpResponse and is actually quite simple:
class HttpResponseServerError(HttpResponse):
status_code = 500
So let's look at the HttpResponse constructor:
def __init__(self, content='', *args, **kwargs):
super(HttpResponse, self).__init__(*args, **kwargs)
# Content is a bytestring. See the `content` property methods.
self.content = content
As you can see by default content is empty.
Now, let's take a look at how it is called by Django itself (an excerpt from django.views.defaults):
def server_error(request, template_name='500.html'):
"""
500 error handler.
Templates: :template:`500.html`
Context: None
"""
try:
template = loader.get_template(template_name)
except TemplateDoesNotExist:
return http.HttpResponseServerError('<h1>Server Error (500)</h1>')
return http.HttpResponseServerError(template.render(Context({})))
As you can see when you produce a server error, the template named 500.html is used, but when you simply return HttpResponseServerError the content is empty and the browser falls back to it's default page.
Put this below in the urls.py.
#handle the errors
from django.utils.functional import curry
from django.views.defaults import *
handler500 = curry(server_error, template_name='500.html')
Put 500.html in your templates. Just as simple like that.
Have you tried with another browser ? Is your custom error page larger than 512 bytes ? It seems some browsers (including Chrome) replace errors page with their own when the server's answer is shorter than 512 bytes.
As of Django 2+ all you need to do is put the corresponding error templates in your base templates folder. Django will automatically render them before rendering the default.
https://docs.djangoproject.com/en/2.0/ref/views/#error-views
In your case just drop your template '500.html' (or '404.html') into /templates
Related
I'm using the following context processor to pass context to all my pages, except when the user is not logged in which case it takes him to the login page :
from django.conf import settings
from .models import foo
from django.shortcuts import redirect
def globalVar(request):
if not request.session.get('user_id') is None:
return {"foo": foo}
else:
return redirect('login')
But the redirect causes an exception for which i didn't find a fix :
ValueError: dictionary update sequence element #0 has length 0; 2 is required
Am i not looking in the right place, or is there a way to replace redirect by something else ?
You can't simply return a redirect from a context processor, since context processors need to return a dict of context variables.
If you really need this functionality in a context processor, you would need to
define a custom exception class (that e.g. wraps the response)
raise such an exception from the context processor (since you can't return a non-dictionary)
catch the custom exception in a Django middleware, unwrap the response and return it
(as an aside, I've written a package that does that – django-safespace)
Instead, if you can, use the login_required decorator (or CBV mixin – see the linked documentation) to make your entire view logged-in-user only.
EDIT: The above answer assumed something else so I've deleted and replaced with an elaboration of why returning redirect doesn't work in case you want to understand
context = {}
redirect = redirect('login')
context.update(redirect)
This should cause the same error you're getting. Got the idea?
I want to redirect URL without slug, to the one with slug, at the urls.py level.
My endpoints looks as follows:
(r'/invoices/<:(-?\d+)>/print/', PrintHandler, 'ShortPrintHandler')
(r'/invoices/<:(-?\d+)>/<:([\w-]*)>/print/', PrintHandler, 'FullPrintHandler')
Is there any way I can pass first, decimal, argument from short URL to the long one, on redirect? Generating URLs without slug is already covered at handler level.
Tried to handle it with
RedirectRoute(r'/invoices/<:(-?\d+)>/print/', PrintHandler, redirect_to_name='FullPrintHandler')
But an error was thrown:
KeyError: 'Missing argument "1" to build URI.'
You can't do that with just a RedirectRoute; you need to get the slug value from somewhere.
You'll need to write a standard route, and in the handler you should get the object from the datastore and return a redirect to the full path using the slug.
Something like (untested):
class RedirectToFullPath(webapp2.RequestHandler):
def get(self, invoice_id):
invoice = Invoice.get_by_id(invoice_id)
self.redirect_to('FullPrintHandler', invoice_id, invoice.slug)
During development, I am running Django in Debug mode and I am posting data to my application using a text mode application. Ideally, I need to receive a plain text response when I get an http error code 500 so I don't have to look for the real error inside all that HTML and Javascript.
Is it possible to obtain a Django 500 Internal Server Error as plain text?
If you are looking for a way to get a plain text error page when using curl, you
need to add the HTTP header X-Requested-With with value XMLHttpRequest, e.g.
curl -H 'X-Requested-With: XMLHttpRequest' http://example.com/some/url/
Explanation: this is because Django uses the is_ajax method to determine whether or not to return as plain text or as HTML. is_ajax in turn looks at X-Requested-With.
Update
Since django version 3.1, error reporting ignores the X-Requested-With header. Instead, set the Accept header on the request to any valid value which does not include the text/html mime type.
e.g.
curl -H 'Accept: application/json;charset=utf-8' http://example.comp/some/url
I think to write a middleware, because otherwise the exception isn't available in the 500.html
http://docs.djangoproject.com/en/dev/topics/http/middleware/#process-exception
class ProcessExceptionMiddleware(object):
def process_exception(self, request, exception):
t = Template("500 Error: {{ exception }}")
response_html = t.render(Context({'exception' : exception }))
response = http.HttpResponse(response_html)
response.status_code = 500
return response
There's a setting DEBUG_PROPAGATE_EXCEPTIONS which will force Django not to wrap the exceptions, so you can see them, e.g. in devserver logs.
This is an improvement on Yuji's answer, which provides a stacktrace, more instructions (for us django newbies) and is simpler.
Put this code in a file somewhere in your application, e.g. PROJECT_ROOT/MAIN_APP/middleware/exceptions.py, and make sure you have an empty __init__.py in the same directory.
import traceback
from django.http import HttpResponse
class PlainExceptionsMiddleware(object):
def process_exception(self, request, exception):
return HttpResponse(traceback.format_exc(exception), content_type="text/plain", status=500)
Now edit your settings.py and find MIDDLEWARE_CLASSES = (. Add another entry so it is like this:
MIDDLEWARE_CLASSES = (
# (all the previous entries)
# Plain text exception pages.
'MAIN_APP.middleware.exceptions.PlainExceptionsMiddleware',
)
Restart django and you are good to go!
User-agent aware formatting.
If you're like me and developing an app and a website both backed by django, you probably want to show plain text error pages to the app, and the nice formatted ones to the browser. A simple way to to that is to check the user agent:
import traceback
from django.http import HttpResponse
class PlainExceptionsMiddleware(object):
def process_exception(self, request, exception):
if "HTTP_USER_AGENT" in request.META and "chrome" in request.META["HTTP_USER_AGENT"].lower():
return
return HttpResponse(traceback.format_exc(exception), content_type="text/plain", status=500)
Building off of Timmmm's answer, I had to make several modifications for it to work in Django 3.1:
Create a file somewhere in your application, such as YOUR_APP_NAME/middleware/exceptions.py and paste the following code:
import traceback
from django.http import HttpResponse, HttpRequest
class PlainExceptionsMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
return self.get_response(request)
def process_exception(self, request: HttpRequest, exception: Exception):
if "HTTP_USER_AGENT" in request.META and "chrome" in request.META["HTTP_USER_AGENT"].lower():
return
print(traceback.format_exc())
return HttpResponse(repr(exception), content_type="text/plain", status=500)
It is not necessary to create an __init__.py file in the middleware folder.
In settings.py, add the following item to the end of the MIDDLEWARE variable, so that it looks like:
MIDDLEWARE = [
# ...
'YOUR_APP_NAME.middleware.exceptions.PlainExceptionsMiddleware'
]
Now, if "HTTP_USER_AGENT" and "chrome" are in the request header, this middleware doesn't do anything, so Django returns an HTML response as usual. Otherwise, it returns a plain-text representation of the error as a response (e.g., ValueError("Field 'id' expected a number but got 'undefined'.")) and prints out the traceback to the Django console, as Django normally would. Of course, you can instead return the full traceback as your response.
I use require_http_methods for restricting access to views. Here example my code:
# myapp/views.py
#require_http_methods(["POST"])
def my_view(request):
return HttpResponse('my_view')
But when I go to url /myapp/my_view I see white page. So, my question is: How I can set default view if method not match rules? For example I want to show 404, 403 or something else. It is possible? Can you provide me a small example? Thank you!
In your case 405 HTTP error is returned.
You could try to create middleware for custom errors:
from django.http import HttpResponseNotAllowed
class CustomHTTPErrorsMiddleware(object):
def process_response(self, request, response):
if isinstance(response, HttpResponseNotAllowed):
# Custom response for `HttpResponseNotAllowed`.
pass
return response
Also, Django's error handling may be useful.
I have a custom 404 view defined in my Pyramid app:
#view_config(context=HTTPNotFound, renderer='404.pt')
def not_found(self, request):
return {}
It works fine, except that the HTTP status code sent with the content is 200 OK, which is not OK by any means. I'm having the same problem with 403 Forbidden. How can I get Pyramid to send the correct status code?
The exception view is a separate view that provides a spot for you to do whatever you want. Just like any view that uses a renderer, you can affect the response object via request.response to modify its behavior. The renderer then fills in the body.
#view_config(context=HTTPNotFound, renderer='404.pt')
def not_found(self, request):
request.response.status = 404
return {}
Actually, in pyramid 1.3 There's a new decorator #notfound_view_config.
#notfound_view_config(renderer = '404_error.jinja2')
def notfound(request):
request.response.status = 404
The best way to do this is to override the default Not Found View:
http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/narr/hooks.html#changing-the-not-found-view
Even in this scenario, you need to return a proper response object which has a status of 404:
def notfound(request):
return Response('Not Found, dude', status='404 Not Found')
To take the example from the page linked above
Here is how you can directly use the 404 hook and render a template while doing so.
In your init.py:
config.add_notfound_view(not_found)
In your view.py:
from pyramid.view import notfound_view_config
from pyramid.renderers import render_to_response
def not_found(request):
request.response.status = 404
t = 'talk_python_to_me_com:templates/errors/404.pt'
return render_to_response(t, {}, request)
I did this for Talk Python To Me: http://www.talkpythontome.com/, here's an invalid page to see a custom template rendered.
http://www.talkpythontome.com/there_is_no_cat