I have a 404.html page, but in some cases I want to be able to send a json error message (for 404 and 500, etc.). I read the following page:
https://docs.djangoproject.com/en/dev/topics/http/views/#the-404-page-not-found-view
Is there any sort of example that shows the implementation? I have it in my urls.py but it's not being picked up in the event of an error.
This worked for me:
from django.conf.urls import patterns, include, url
from django.views.static import *
from django.conf import settings
from django.conf.urls.defaults import handler404, handler500
from app.views import error
urlpatterns = patterns('',
# Examples:
# url(r'^$', 'app.views.home', name='home'),
)
handler404 = error.error_handler
handler500 = error.error_handler
You can make it do anything as you wish when going to that controller.
In addition to the previous answer, it is important to say that the views.py should return a HttpResponse with a 404 status in the http header. It is important to inform the search engines that the current page is a 404. Spammers sometimes creates lots of urls that could seem that would lead you to some place, but then serves you another content. They frequently make lots of different addresses serve you almost the exact same content. And because it is not user friendly, most SEO guide lines penalize that. So if you have lots of addresses showing the same pseudo-404 content, it could not look good to the crawling systems from the search websites. Because of that you want to make sure that the page you are serving as a custom 404 has a 404 status. So here it is a good way to go:
Into your application's urls.py add:
# Imports
from django.conf.urls.static import static
from django.conf.urls import handler404
from django.conf.urls import patterns, include, url
from yourapplication import views
##
# Handles the URLS calls
urlpatterns = patterns('',
# url(r'^$', include('app.homepage.urls')),
)
handler404 = views.error404
Into your application's views.py add:
# Imports
from django.shortcuts import render
from django.http import HttpResponse
from django.template import Context, loader
##
# Handle 404 Errors
# #param request WSGIRequest list with all HTTP Request
def error404(request):
# 1. Load models for this view
#from idgsupply.models import My404Method
# 2. Generate Content for this view
template = loader.get_template('404.htm')
context = Context({
'message': 'All: %s' % request,
})
# 3. Return Template for this view + Data
return HttpResponse(content=template.render(context), content_type='text/html; charset=utf-8', status=404)
The secret is in the last line: status=404
Hope it helped!
I look forward to see the community inputs to this approach. =)
Basics:
To define custom view for handling 404 errors, define in the URL config, a view for handler404, like handler404 = 'views.error404'
Apart from the basics, some things to note about (custom 404 views):
It will be enabled only in Debug=False mode.
And more ignored one, across most answers (and this this stuck my brains out).
The 404 view defaults to
django.views.defaults.page_not_found(request, exception, template_name='404.html')
Notice the parameter exception
This was causing a 404 to 500 redirect from within def get_exception_response(self, request, resolver, status_code, exception) function defined in core.handlers.base since it could not find the parameter exception
Related
I have quation regarding Django, I tried to solve it myself during 2 days, but need some help.
I read a book about Django, and there is example:
urls.py
from django.urls import re_path
from django.urls import path
from firstapp import views
urlpatterns = [
re_path(r'^products/?P<productid>\d+/', views.contact),
re_path(r'^users/(?P<id>\d+)/?P<name>\D+/', views.about),
re_path(r'^about/contact/', views.contact),
re_path(r'^about', views.about),
path('', views. index),
]
views.py
from django.http import HttpResponse
def index(request):
return HttpResponse("<h2>Main</h2>")
def about(request):
return HttpResponse("<h2>About site</h2>")
def contact(request):
return HttpResponse("<h2>Contacts</h2>")
def products(request, productid):
output = "<h2>Product № {0}</h2>".format(productid)
return HttpResponse(output)
def users(request, id, name):
output = "<h2>User</h2><h3>id: {О} " \
"Name:{1}</hЗ>".format(id, name)
return HttpResponse(output)
But after using this link http://127.0.0.l:8000/products/5, I get this text:
Using the URLconf defined in hello.urls, Django tried these URL patterns, in this order:
^products/?P<productid>\d+/
^users/(?P<id>\d+)/?P<name>\D+/
^about/contact/
^about
The current path, products/5, didn’t match any of these.
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.
And this thing in terminal:
Not Found: /products/5
[08/Feb/2023 12:17:13] "GET /products/5 HTTP/1.1" 404 2597
Haven't tested this solution myself yet - but it looks like you're missing some parentheses. Also the product mapping is wrong (should be views.products instead of views.contact).
I believe the first path you've configured should be something like:
re_path(r'^products/(?P<productid>\d+)/', views.products),
This should work, but will match products/5 as well as products/5/anything/else/here which is probably not the behaviour you're after. In that case the path should be something like:
re_path(r'^products/(?P<productid>\d+)/$', views.products),
The django docs do a pretty good job at explaining how all this works in any level of detail you need, see https://docs.djangoproject.com/en/4.1/ref/urls/#re-path
My Django (2.2.5) app has the following urls.py:
from django.urls import path
from django.urls.conf import re_path
from django.views.generic.base import TemplateView
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from . import views
urlpatterns = [
re_path('^$|signin', TemplateView.as_view(template_name='signin.html'), name='signin'),
path('forgot', TemplateView.as_view(template_name='forgot.html'), name='forgot_pass'),
path('app', TemplateView.as_view(template_name='core.html'), name='core'),
path('try_signin', views.try_signin, name='try_signin'),
] + staticfiles_urlpatterns()
The first 3 rules work fine and serve up the respective HTML content. The 4th rule is for a POST request, but the request causes the following error:
Method Not Allowed (POST): /try_signin
Method Not Allowed: /try_signin
[30/Sep/2019 14:20:38] "POST /try_signin HTTP/1.1" 405 0
However if I reorder the URL rules so that the POST rule comes first, then it works fine. There's no conflict in the rules that I can see. I'm new to Django and still learning but I'd like to understand why re-ordering the rules avoids the error, or if there's something else I'm doing/not doing that caused the error.
This is my views.py:
from django.http.response import JsonResponse
from time import sleep
import logging
import json
log = logging.getLogger(__name__)
def try_signin(request):
user_email = request.POST.get('user', None)
password = request.POST.get('pass', None)
log.info("Signin attempt ==> [%s] [%s]" % (user_email, password))
sleep(2)
data = {
'success': False
}
log.info("Returning response ==> %s" % json.dumps(data))
return JsonResponse(data)
Also, adding #require_POST decorator to the try_signin function above still causes the error. As I said earlier, it does work if I reorder the rule to appear first in url_patterns.
re_path('^$|signin', ...) matches the url /try_signin. So when you POST to this URL, it goes to the TemplateView for signin.html which only accepts GET requests.
My django site is having web & mobile versions. I have enabled debug setting false which returns templates 404.html whenever the requested page is not found. I would like to modify the view function to return 2 different 404 html pages like 404.html/404mobile.html based on platform.
Detecting user browser through JavaScript in 404.html page did not help as my 404.html page has header and footer extends from base html file.
Modifying views will solve this? If so where is the debug setting class file residing in Django package?
Here is the function using user_agent ..
from user_agents import parse
def idfy(request):
user_agent = parse(ua_string)
if user_agent.is_mobile:
return HttpResponse('I m n mobile')
else:
return HttpResponse('I m n pc')
from django.conf.urls import patterns, include, url
from django.views.static import *
from django.conf import settings
from django.conf.urls.defaults import handler404
from app.views import error
urlpatterns = patterns('',
# Examples:
# url(r'^$', 'app.views.home', name='home'),
)
handler404 = error.error_handler
overriding handler404 method resolves this issue
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)
))
I'm going through the Django tutorial and am on part 5: Testing. I run into the problem where I'm using the DetailView and ListView "shortcut" views to factor out code (as suggested by the tutorial), but when a 404 page is displayed, a 200 status code is returned instead. Am I doing something wrong? The tutorial says the status code should be 404.
Thanks!
You need to define the Http header to have a 404 status.
return HttpResponse(content=template.render(context), content_type='text/html; charset=utf-8', status=404)
It is important to inform the search engines that the current page is a 404. Spammers sometimes creates lots of urls that could seem that would lead you to some place, but then serves you another content. They frequently make lots of different addresses serve you almost the exact same content. And because it is not user friendly, most SEO guide lines penalize that. So if you have lots of addresses showing the same pseudo-404 content, it could not look good to the crawling systems from the search websites. Because of that you want to make sure that the page you are serving as a custom 404 has a 404 status.
If you are trying to make a custom 404 page, here it is a good way to go:
Into your application's urls.py add:
# Imports
from django.conf.urls.static import static
from django.conf.urls import handler404
from django.conf.urls import patterns, include, url
from yourapplication import views
##
# Handles the URLS calls
urlpatterns = patterns('',
# url(r'^$', include('app.homepage.urls')),
)
handler404 = views.error404
Into your application's views.py add:
# Imports
from django.shortcuts import render
from django.http import HttpResponse
from django.template import Context, loader
##
# Handle 404 Errors
# #param request WSGIRequest list with all HTTP Request
def error404(request):
# 1. Load models for this view
#from idgsupply.models import My404Method
# 2. Generate Content for this view
template = loader.get_template('404.htm')
context = Context({
'message': 'All: %s' % request,
})
# 3. Return Template for this view + Data
return HttpResponse(content=template.render(context), content_type='text/html; charset=utf-8', status=404)
The secret is in the last line: status=404
Hope it helped!
I look forward to see the community inputs to this approach. =)
You can
return HttpResponseNotFound(render_to_string('404.html'))
instead.