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.
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
I have two apps:
backend
shop
I my urls in main app dir:
path('backend/', include('backend.urls')),
path('', include('shop.urls')),
the problem is if I write in my url: localhost:8000/backend/abc which not exist Django jumps over to shop.urls and the app is crashing because it can not find the slug and the query goes in fail.
How can I prevent if I go to the url /backend/somethingwhichnotexist is returning an 404 and not search in other app urls for this folder? I have thought that this is one of the main reason for split the urls in app folders.
Here are some urls from backend/urls.py:
from django.urls import path, re_path
from . import views as backend_views
from django.contrib.auth import views as auth_views
from froala_editor import views
from django.conf.urls import include
urlpatterns = [
path('stamdata/', backend_views.edit_masterdata),
path('praefikser/', backend_views.edit_prefixes),
path('leverandorer/', backend_views.suppliers_view),
path('leverandorer/add', backend_views.add_supplier),
]
handler404 = 'backend.views.page_not_found_view'
shop/urls.py
here the url stops:
path('<slug:category_slug>/<slug:slug_subcategory>/', butik_views.cat_or_article),
normally I don't want that a backend urls switches to frontend view
regards
Christopher.
You you said Django run through all of the URL patterns and stops till it finds its matching URL. SO, what has happen here is that when you type a URL with localhost:8000/backend/abc/ it runs through all the URLS before returning 404 to the client. So, it stops at the URL that support any string with 2 parameters in your case below URL.
path('<slug:category_slug>/<slug:slug_subcategory>/', butik_views.cat_or_article),
To get a 404 by adding a static word in you URL.
path('shop/<slug:category_slug>/<slug:slug_subcategory>/', butik_views.cat_or_article),
or
path('backend/', include('backend.urls')),
path('shop/', include('shop.urls')),
For now I fixed it with
shop/views.py
if category_slug == 'backend':
response = render(
request,
'backend/404.html',
)
response.status_code = 404
return response
else:
until I found another solution. It looks like that the URL dispatcher is working like this:
Django runs through each URL pattern, in order, and stops at the first
one that matches the requested URL, matching against path_info.
So for now I did not found another way.
Edit:
I added this in the end of backend/urls.py:
re_path(r'^.*/$', backend_views.page_not_found_view)
so after all working urls it goes to all other to the 404 view.
I have a django server and an angular app.
I am able to launch the angular app and navigate to the various parts of the site using links i have added to the app.
But if I want to go to a part of the site directly I get a 404 error.
example if I have a link in my app that directs me to
niftysite.com/team
it works
but if I put in my browsers url
niftysite.com/team
it fails with 404
I understand this is because the default behavior is to look for the link to the page in the servers urls.
I also understand that the fix is to have server side 404s redirect to the angular index.html page with the route params included.
My question is how?
I have already started on an implementation but I am not sure how to fix it. Any guidance the rest of the way through would be helpful.
here is what I have so far.
urls.py
handler404 = views.error_404
views.py
from django.shortcuts import render
def error_404(request):
data = {}
return render(request, 'index.html', data)
similar to this question, but I am not using Nginx
How to redirect 404 requests to homepage in Django single page app using Nginx?
Here is the implementation:
urls.py
url(r'^.*$', views.PassToAngular.as_view())
make sure this url is the last url in your urls array.
views.py
from django.shortcuts import render
from django.views.generic import TemplateView
class PassToAngular(TemplateView):
def get(self, request, **kwargs):
return render(request, 'index.html', context= None)
thanks to MihirKavatkar for your help!
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 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