Django display 404 on missing template - django

I have a website where some pages are edited by hand. When one of those templates is missing, it just means that the page is not present, so I would like to display an Error 404.
Instead I get an exception TemplateDoesNotExist.
Is there a way to tell Django to display an error 404 whenever it does not find a template?

If you want this behaviour for all views on your site, you might want to write your own middleware with a process_exception method.
from django.template import TemplateDoesNotExist
from django.views.defaults import page_not_found
class TemplateDoesNotExistMiddleware(object):
"""
If this is enabled, the middleware will catch
TemplateDoesNotExist exceptions, and return a 404
response.
"""
def process_exception(self, request, exception):
if isinstance(exception, TemplateDoesNotExist):
return page_not_found(request)
If you have defined your own handler404 you would need to replace page_not_found above. I'm not immediately sure how you could convert the string handler404 into the callable required in the middleware..
To enable your middleware, add it to MIDDLEWARE_CLASSES in settings.py. Be careful of the position where you add it. The standard Django middleware warning applies:
Again, middleware are run in reverse order during the response phase, which includes process_exception. If an exception middleware returns a response, the middleware classes above that middleware will not be called at all.

put the return of the response in the view (or whereever the template is rendered) in a try-except block:
from django.http import Http404
from django.shortcuts import render_to_response
from django.template import TemplateDoesNotExist
def the_view(request):
...
try:
return render_to_response(...)
except TemplateDoesNotExist:
raise Http404

Off the top of my head, but if you set DEBUG=False in your settings, won't you get a 404 then on every error (including TemplateNotFound)?

Related

using Handler404 in django url dispatcher cause server error

I followed this https://stackoverflow.com/a/31381075/10087274 because I want when url does not exist a json shows error but the problem is here that I get server error 500 when I add handler404 in my url dispatcher
here is my project url :
from django.urls import path, include
from django.conf.urls import handler404
from api.exception import custom404
handler404 = custom404
urlpatterns = [
path('api/v1/', include('acl.urls')),
]
and I have exception.py inside my project folder (near settings.py) contains this:
from django.http import JsonResponse
def custom404(request):
return JsonResponse({
'status_code': 404,
'error': 'The resource was not found'
})
I dont know how can I solve my problem
Unfortunately, you haven't provided much information regarding the error traceback. Anyway, The very first thing I noticed in your code, you've missed one optional parameter to the custom404 function. That function should take two parameters, request and exception
def custom404(request, exception=None):
response = {
'status_code': 404,
'error': 'The resource was not found'
}
return JsonResponse(response, status=404)
Reference
1. Custom Error Views
Well django rest framework is open source, so if you want to replicate certain behaviour you can read the code and pick and chose what you like. For instance, you can see that the Generic Error view (custom server and bad request error views) provided in drf docs are located inside exceptions.py inside rest_framework you can look it up here and see how it can be done.
Create a custom 404 view, like this:
def not_found(request, exception, *args, **kwargs):
""" Generic 404 error handler """
data = {
'error': 'Not Found (404)'
}
return JsonResponse(data, status=status.HTTP_404_NOT_FOUND)

Django - get_object_or_404 returns 500 instead of 404

I was trying to test get_object_or_404 method in my view. So I set DEBUG=False and set ALLOWED_HOSTS=['*'].
Now, when I go to http://127.0.0.1:8000/profile/correctusername/, it returns a correct profile. The problem is that if I try to write incorrect username, it returns 500 instead of 404 - according to name of the functio I suppose that it should return 404.
def get_user_profile(request, username):
# user = User.objects.get(username=username)
user = get_object_or_404(User, username=username)
jobs = user.jobs.all()
table = MyJobsTable(jobs)
context = {
'my_jobs': table,
"user_": user
}
return render(request, 'auth/profiles/my-profile.html', context=context)
Why is that so and how to fix it?
I encountered the same problem and Milano Slesarik's comment helped me figure out the solution. It turns out that I recently assigned a custom 404 handler but didn't do it correctly.
Here's what I did right:
add following line to urls.py. No need to add from django.conf.urls import handler404:
handler404 = views.error404
Here's what I did wrong. In my views.py I added this function:
def error404(request):
return HttpResponseNotFound('custom 404 not found')
But I forgot to import HttpResponseNotFound in views.py:
from django.http import HttpResponse, HttpResponseNotFound
So it was raising an exception. But since I just set DEBUG=False I couldn't see the error. I just saw the 500 response code.
Hope that helps someone!
with the following line in urls.py:
handler404 = 'views.error404'
If the project has several apps with their own urls.py you should add handler404 = 'app_name.views.my_handle404' to to the root url conf (that exists in project directory by default)
For me the only solution that worked (Django 3.7) dev/prod is here
Please see #elano7 answer

Best way to handle legacy urls in django

I am working on a big news publishing platform. Basically rebuilding everything from ground zero with django. Now as we are almost ready for the launch I need to handle legacy url redirects. What is the best way to do it having in mind that I have to deal with tenths of thousands of legacy urls?
Logic should work like this: If none of existing urls/views where matched run that url thorough legacy Redirect urls patterns/views to see if it can provide some redirect to the new url before returning 404 error.
How do I do that?
You may want to create a fallback view that will try to handle any url not handled by your patterns. I see two options.
Just create a "default" pattern. It's important to this pattern to
be the last within your urlpatterns!
in your urls.py:
urlpatterns = patterns(
'',
# all your patterns
url(r'^.+/', 'app.views.fallback')
)
in your views.py:
from django.http.response import HttpResponseRedirect, Http404
def fallback(request):
if is_legacy(request.path):
return HttpResponseRedirect(convert(request.path))
raise Http404
Create a custom http 404 handler.
in your urls.py:
handler404 = 'app.views.fallback'
in your views.py
from django.http.response import HttpResponseRedirect
from django.views.defaults import page_not_found
def fallback(request):
if is_legacy(request.path):
return HttpResponseRedirect(convert(request.path))
return page_not_found(request)
it may seem to be a nicer solution but it will only work if you set DEBUG setting to False and provide custom 404 template.
Awesome, achieved that by using custom middleware:
from django.http import Http404
from legacy.urls import urlpatterns
class LegacyURLsMiddleware(object):
def process_response(self, request, response):
if response.status_code != 404:
return response
for resolver in urlpatterns:
try:
match = resolver.resolve(request.path[1:])
if match:
return match.func(request, *match.args, **match.kwargs)
except Http404:
pass
return response
Simply add this middleware as a last middleware in MIDDLEWARE_CLASSES list. Then use urls.py file in your legacy app to declare legacy urls and views which will handle permanent redirects. DO NOT include your legacy urls in to main urls structure. This middleware does it for you, but in a bit different way.
Use the Jacobian's django-multiurl. There is a django ticket to address the issue someday, but for now django-multiurl works very good.
Before:
# urls.py
urlpatterns = patterns('',
url('/app/(\w+)/$', app.views.people),
url('/app/(\w+)/$', app.views.place), # <-- Never matches
)
After:
# urls.py
from multiurl import multiurl, ContinueResolving
from django.http import Http404
urlpatterns = patterns('', multiurl(
url('/app/(\w+)/$', app.views.people), # <-- Tried 1st
url('/app/(\w+)/$', app.views.place), # <-- Tried 2nd (only if 1st raised Http404)
catch=(Http404, ContinueResolving)
))

Django raising 404 with a message

I like to raise 404 with some error message at different places in the script eg: Http404("some error msg: %s" %msg)
So, in my urls.py I included:
handler404 = Custom404.as_view()
Can anyone please tell me how should I be handling the error in my views. I'm fairly new to Django, so an example would help a lot.
Many thanks in advance.
Generally there should not be any custom messages in 404 errors bu if you want to implement it you can do this using django middlewares.
Middleware
from django.http import Http404, HttpResponse
class Custom404Middleware(object):
def process_exception(self, request, exception):
if isinstance(exception, Http404):
# implement your custom logic. You can send
# http response with any template or message
# here. unicode(exception) will give the custom
# error message that was passed.
msg = unicode(exception)
return HttpResponse(msg, status=404)
Middlewares Settings
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'college.middleware.Custom404Middleware',
# Uncomment the next line for simple clickjacking protection:
# 'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
This will do the trick. Correct me if I am doing any thing wrong. Hope this helps.
In general, 404 error is "page not found" error - it should not have customizable messages, simply because it should be raised only when a page is not found.
You can return a TemplateResponse with status parameter set to 404
Raise an Http404 exception inside a view. It's usually done when you catch a DoesNotExist exception. For example:
from django.http import Http404
def article_view(request, slug):
try:
entry = Article.objects.get(slug=slug)
except Article.DoesNotExist:
raise Http404()
return render(request, 'news/article.html', {'article': entry, })
Even better, use get_object_or_404 shortcut:
from django.shortcuts import get_object_or_404
def article_view(request):
article = get_object_or_404(MyModel, pk=1)
return render(request, 'news/article.html', {'article': entry, })
If you'd like to customize the default 404 Page not found response, put your own template called 404.html to the templates folder.
Yes we can show specific exception message when raise Http404.
Pass some exception message like this
raise Http404('Any kind of message ')
Add 404.html page into templates directory.
templates/404.html
{{exception}}
I figured out a solution for Django 2.2 (2019) after a lot of the middleware changed. It is very similar to Muhammed's answer from 2013. So here it is:
middleware.py
from django.http import Http404, HttpResponse
class CustomHTTP404Middleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before the view (and later middleware) are called.
response = self.get_response(request)
# Code to be executed for each request/response after the view is called.
return response
def process_exception(self, request, exception):
if isinstance(exception, Http404):
message = f"""
{exception.args},
User: {request.user},
Referrer: {request.META.get('HTTP_REFERRER', 'no referrer')}
"""
exception.args = (message,)
Also, add this last to your middleware in settings.py: 'app.middleware.http404.CustomHTTP404Middleware',
if you want to raise some sort of static messages for a particular view , you can do as follows:-
from django.http import Http404
def my_view(request):
raise Http404("The link seems to be broken")
You can return a plain HttpResponse object with a status code (in this case 404)
from django.shortcuts import render_to_response
def my_view(request):
template_context = {}
# ... some code that leads to a custom 404
return render_to_response("my_template.html", template_context, status=404)
In my case, I wanted to take some action (e.g. logging) before returning a custom 404 page. Here is the 404 handler that does it.
def my_handler404(request, exception):
logger.info(f'404-not-found for user {request.user} on url {request.path}')
return HttpResponseNotFound(render(request, "shared/404.html"))
Note that HttpResponseNotFound is required. Otherwise, the response's HTTP status code is 200.
The default 404 handler calls 404.html . You could edit that if you don't need anything fancy or can override the 404 handler by setting the handler404 view -- see more here

Implementing http404 in django

Implementing page not found in django and have looked at the documentation 404
I do not get a page not found error as yet what ami doing here
In my code in urls i have done the following,
url(r'^$', 'site_config.views.pagenotfound')
from django.http import Http404
def pagenotfound(request):
return render_to_response('polls/pagenotfound.html', {})
The way you handle 404 and 500 in django is: by default, in the templates directory, create a 404.html
If you need a custom handler, just add these to urls.py
handler404 = 'views.page_not_found_custom'
handler500 = 'views.page_error_found_custom'
design the 404.html page the way you want