Django handler404 not called on raise Http404() - python

When i override the handler404 and handler500 in my urls.py like so:
from myapp.views import not_found_view
handler404 = not_found_view
handler500 = not_found_view
and call raise Http404() in my middleware, i see my 404 page.
When i remove both handler404 and handler500 from my urls.py and raise Http404() in my middleware, i see my default 500 error page (from Django Suit) - hence the reason i'm trying to set a custom 404 template to be used on raise Http404()'s.
Now my problem: when i remove the handler500 and only set the handler404, i also get to see a HTTP 500 page! :|
Why is my handler500 called when i raise Http404() ??
I have multiple template directories in multiple apps, i tried placing a 404.html in my main app that contains the settings and in another 'normal' app but that didn't work..
I've set DEBUG to False as well.
Hope someone can answer my question!

You don't have to write your own handlers if you just want to replace Django's template. Django uses 404.html and 500.html files for templates and you can replace them with putting file with same name to the directory listed in settings.TEMPLATE_DIRS.
In your example that means that you can create myapp/templates/404.html and myapp/templates/500.html files.
Note that you have to set DEBUG to False to se those templates.
Check this article for mor details.

I ran into a similar situation. My custom handler404 WAS being called, yet my browser response showed the error500.
Upon inspection, I found I had a syntax error in the template. Thus, the 500 was being caused by that. I fixed the template and the result was as desired.
The catch 22 is that by default, template debug == debug, so if you set DEBUG=False then the template debug will be false as well; you'll see the 500 error without the template error trace.
So, place a print() statement in your handler404 view, right before returning render() . If you see the output in your django console, you'll know the error is in your template.
You could also try this in your settings.py (django 1.8+) but this didn't seem to work for me, and I didn't follow through once I fixed the template.
TEMPLATES = [
{
'OPTIONS': {
'debug': True,
}
},

The 404-page-not-found-view needs an "exception" argument:
def handler404(request, exception):
return render(request, 'customer/home_page/404.html', status=404)

Another solution is to replace (monkey-patch) Django's page_not_found.
from django.views import defaults
from myapp.views import not_found_view
defaults.page_not_found = not_found_view
Monkey-patching is bad practice and may stop working if Django updates internal API, but still may be useful in some situations. Ex. if other methods don't work on some reason.

Related

LocaleMiddleware redirects on a non-i18n url pattern

I'm using Django's LocaleMiddleware to internationalize a part of the website I'm working on.
Here is my project's urls.py:
from django.conf.urls import patterns, include, url
from django.conf.urls.i18n import i18n_patterns
urlpatterns = patterns('',
url(r'^api/stuff/(?P<stuff_id>)\d+/$', ApiStuff.as_view()),
)
urlpatterns += i18n_patterns('',
url(r'^stuff/', DoStuff.as_view()),
)
The problem is, when ApiStuff.as_view() returns a 404 response (other error codes behave as expected), the LocaleMiddleware operates the request to make it redirect to /en/api/stuff/<stuff_id>, even though the /api namespace is clearly not in the i18n_patterns (at the end, it generates a 404 error too, but the content of my original response is lost).
Here is the code of ApiStuff:
import django.http
from django.views.generic import View
from project.stuff.models import Stuff
class ApiStuff(View):
#staticmethod
def get(request, *args, **kwargs):
stuff_id = kwargs['stuff_id']
try:
stuff = Stuff.objects.get(pk=stuff_id)
except Stuff.DoesNotExist:
return response({"error": "stuff not found"}, 404)
result = stuff.serialize()
return response(result)
def response(data, status=200):
data = json.dumps(data)
return django.http.HttpResponse(data, status=status, content_type='application/json')
I'm using django 1.6.10 (I know, it's late, but I can't update the version right now).
Am I missing something?
This is an old bug that has been tracked here:
https://code.djangoproject.com/ticket/17734
The 404 error handler is a default handler of Django. It obviously takes the current language into account in order to translate the "not found" message.
Unless you can upgrade to a newer version you probably need to define your own error handler (including route/url).
EDIT:
As a workaround it might be possible to extend the LocaleMiddleware and maybe also CommonMiddleware. However, it could be that the 404 handler does its magic nevertheless. Might be worth a shot though because it should only require few lines of code.
Citing the ticket:
when you request /api/raising/404/, it is redirecting the user to /en/api/raising/404/ (assuming it detects the language as en). If it tests before redirecting the user if /en/api/raising/404/ is resolvable, it would find out that this would raise a 404 as well and thus would not redirect the user.
EDIT 2:
As an alternative you could simply not use i18n_patterns and just detect/switch the language via browser/cookie/parameter. I did that for a project (which included Django CMS) where I kept running into problems with these URLs. It is definitely not a prerequisite for localized Django sites to use i18n URL patterns. It's just something on top, but not at all necessary. Localization will work just fine without it. If you need help with the middleware for switching languages, drop a comment.
One can get around this issue by using following url config
urlpatterns += i18n_patterns(
url(r'^(?!api.*)stuff', DoStuff.as_view()),
)

How to link django to my own 404 error page?

Normaly django looks for a url in url.py file...
if it finds it load the relevent HTML page and if it doesn't find it shows:
Page not found (404) with the msg:
You're seeing this error because you have DEBUG = True in your Django
settings file. Change that to False, and Django will display a
standard 404 page.
I want to change standard 404 page that django shows. I have a HTML page that I created PageNotFound.html I want django to show everytime there is a Page not found (404) when DEBUG = False. How do I do that?
In order to show customized HTML when Django returns a 404, you can create an HTML template named 404.html and place it in the top level of your template tree. This template will then be served when DEBUG is set to False.
https://docs.djangoproject.com/en/dev/topics/http/views/#the-404-page-not-found-view
Also, there is an useful section "Customizing error views".
And in the case you want to test your template withour turning DEBUG=False, you can run manage.py runserver --insecure.
It will force Django to serving static files with the staticfiles app even if the DEBUG setting is False. But note that it it only for local development and could be insecure. You can read more about this option here.
Here is a quote from the Django doc:
This template should be called 404.html and located in the top level of your template tree.
Check here for the full text; for non 1.8 doc it should be somewhere around here: Writing views > Returning errors > The Http404 exception.
You should create following view:
def custom404(request):
return render(request, '404.html', status=404)
And connect it with following construction in urls.py:
handler404 = 'path.to.views.custom404'

Django 1.8 how to create my own 404 page?

i want to create my own 404 page.
In settings.py I have added:
DEBUG = TEMPLATE_DEBUG = False
ALLOWED_HOSTS = ['*',]
In urls.py:
handler404 = 'blog.views.handler404'
In views.py:
def handler404(request):
return render(request, 'blog/404.html')
Also I have created that 404.html file.
When i start server i write:
python manage.py runserver --insecure
--insecure is to provide static files (otherwise it is nonsense). But if i go non existing page i get:
<h1>Not Found</h1><p>The requested URL /post/9/ was not found on this server.</p>
How do I solve this?
I am using Django 1.8 dunno if this changes anything
You shouldn't need anything in urls.py. Go to your root views.py and add your handler404 method there, and leave urls.py alone.
Ref: Django, creating a custom 500/404 error page
Also, I don't see your TEMPLATE_DIRS variable, i.e.
TEMPLATE_DIRS = (
'/path/to/template/files/',
'/path/to/more/template/files/'
)
Need to make sure that your templates in ../blog/.. are getting found properly. Personally I'd add that specifically as a subdirectory.

Redirect any urls to 404.html if not found in urls.py in django

How can I redirect any kind of url patterns to a created page "404.html" page if it doesn't exist in the urls.py rather than being shown the error by django.
Make a view that'll render your created 404.html and set it as handler404 in urls.py.
handler404 = 'app.views.404_view'
Django will render debug view if debug is enabled. Else it'll render 404 page as specified in handler404 for all types of pages if it doesn't exist.
Django documentation on Customizing error views.
Check this answer for a complete example.
In your views.py, just add the following code (No need to change anything in urls.py).
from django.shortcuts import render_to_response
from django.template import RequestContext
def handler404(request):
response = render_to_response('404.html', {},
context_instance=RequestContext(request))
response.status_code = 404
return response
Put a custom 404.html in templates directory.
source : click here
There is no need to change anything in your view or url.
Just do these 2 steps, in your settings.py, do the following
DEBUG = False
ALLOWED_HOSTS = ["*"]
And in your app directory (myapp in this example), create myapp/templates/404.html where 404.html is your custom error page. That is it.
Go to your project settings.py and set DEBUG = True to DEBUG = False
Then Django redirects all NOT set patterns to not found.
In additional if you want to customize 404 template , in your project urls.py
set
handler404 = 'app.views.404_view'
then in your projects view.py
from django.shortcuts import render_to_response
from django.template import RequestContext
def handler404(request):
response = render_to_response('404.html', {},
context_instance=RequestContext(request))
response.status_code = 404
return response
and Finally, in your templates add 404.html and fill it with what you want to show end user.

Django: information leakage problem when using #login_required and setting LOGIN_URL

I found a form of information leakage when using the #login_required decorator and setting the LOGIN_URL variable.
I have a site that requires a mandatory login for all content. The problem is that you get redirected to the login page with the next variable set when it's a existing page.
So when not logged in and asking for:
http://localhost:8000/validurl/
You see this:
http://localhost:8000/login/?next=/validurl/
And when requesting an non existing page:
http://localhost:8000/faultyurl/
You see this:
http://localhost:8000/login/
Which reveals some information that I dont want. I thought of overriding the login method, forcing the next to empty and calling 'super' on this subclassed method.
An additional problem is that some of my tests fail without the LOGIN_URL set. they redirect to '/accounts/login/' instead of '/login/'. Hence why I'd like to use the LOGIN_URL but disable the 'auto next' feature.
Anybody that can shed some light on the subject?
Thanx a lot.
Gerard.
You can include this line as the last pattern in your urls.py file. It will re-route urls that do not match any other pattern to the login page.
urlpatterns = patterns('',
...
(r'^(?P<path>.+)$', 'django.views.generic.simple.redirect_to', {
'url': '/login/?next=/%(path)s',
'permanent': False
}),
)
EDIT: To keep raising 404 pages to authenticated users, do the following:
from django.http import Http404, HttpResponseRedirect
def fake_redirect(request, path):
if request.user.is_authenticated:
raise Http404()
else:
return HttpResponseRedirect('/login/?next=/%s' % path)
urlpatterns = patterns('',
...
(r'^(?P<path>.+)$', fake_redirect),
)

Categories