The ‘raise Http404’ call finds the wrong 404.html page. Note that I have two apps, each with its own templates directory; each has its own 404.html. My theory is that Django uses the first one it finds, not the one that’s in the same directory as the view.py that calls Http404 (i.e. artwork/views.py does the ‘raise Http’ call, but instead of finding artwork/templates/404.html, it finds catalog/templates/404.html). I tried putting the 404.html in the root directory (akdb) but it doesn’t find it.
Is there a way to force Http404 to look in a specific directory for the html file, or should I just forget it and roll my own?
── akdb
│ ├── akdb
│ ├── artwork
│ │ ├── admin.py
│ │ ├── apps.py
│ │ ├── context_processors.py
│ │ ├── management
│ │ ├── migrations
│ │ ├── models.py
│ │ ├── static
│ │ ├── templates
│ │ │ ├── 404.html
│ │ │ ├── 500.html
│ │ │ └── artwork
│ │ │ ├── base.html
│ │ │ ├── contact.html
│ │ │ ├── cv.html
│ │ │ ├── detail.html
│ │ │ ├── exhibitions.html
│ │ │ ├── group.html
│ │ │ ├── index.html
│ │ │ ├── list.html
│ │ │ └── title.html
│ ├── catalog
│ │ ├── admin.py
│ │ ├── apps.py
│ │ ├── context_processors.py
│ │ ├── forms.py
│ │ ├── management
│ │ ├── models.py
│ │ ├── static
│ │ ├── templates
│ │ │ ├── 404.html
│ │ │ ├── 500.html
│ │ │ └── catalog
│ │ │ ├── add.html
│ │ │ ├── artwork_form.html
│ │ │ ├── base.html
│ │ │ ├── detail.html
│ │ │ ├── edit.html
│ │ │ ├── filter_forms.html
│ │ │ └── index.html
find template process loop template dirs in settings.py
if you have same name templates, use 1 template dir and use subdirectory.
or use other name.
project/
|- templates
|- app1
|- app2
OR, in your views.py
return render({tempaltes}, status_code=404)
I assume you are raising the http404 exception which looks for a 404.html
template.
Django searches first plausible location for 404.html
(usually the project templates folder … then the apps template folders, if that doesn’t exist). Therefore, your approach may be unconventional. How about having one 404.html
template at the recommended location? Then you can provide context for the render like so:
raise Http404("Poll does not exist")
white-seolpyo: Thanks. I tried: return render(“artwork/404.html”, status_code=404) … didn’t find it.
onyeibo: Yes, I’m doing ‘raise Http404’. I tried putting the 404.html in: /templates/404.html (the recommended ‘root’ template directory) but it doesn’t find it.
PS: I’m doing raise Http404 because get_object_or_404 always results in “AttributeError: ‘Manager’ object has no attribute ‘get_object_or_404’” which is a mystery I couldn’t figure out.
I got the search order wrong. Django searches apps/template folders first before project/templates folder assuming you set "APP_DIRS"
to True
under TEMPLATES
in your project/settings.py
The real issue with your file structure is that your app/templates
folders are missing an additional folder/layer.
|- catalog
|- templates
|- catalog --# apps require this layer which is the name of the app
|- catalog_template.html
|- artwork
|- templates
|- artwork # apps require this layer which is the name of the app
|- artwork_template.html
Django is finding the first 404.html
in the paths which is probably the wrong one. If you want Django to locate 404.html
in project/templates folder, set "APP_DIRS": False
Ok, interesting. I’m pretty sure the docs said to put the 404 pages directly into the app/templates dir, not the app/templates/app dir. I think I tried the latter already, but I’ll try it again. Thanks for the suggestion.
did you add (BASE_DIR / artwork/templates) directory in TEMPLATES[“DIRS”]?
in your directory tree, you use “404-artwork.html”, not “artwork/404.html”.(need change template name)
No, the TEMPLATES [‘DIRS’] setting is empty.
"…you use ‘404-artwork.html’ … that doesn’t make sense to me. There’s no file named 404-artwork.html.
yeah, i mean, “artwork/templates/404.html” rename “artwork/templates/404-artwork.html”.
Let me clarify:
I did not mean you should put the 404.html
template in app/templates/app/
… that was misleading andI have reflected the correct thing in my previous reply. I was only saying that apps require a third folder bearing the name of the app. It uses that to differentiate the namespaces. Without that, django will use the first template that matches and that could be from any of the template folders in the project.
Therefore, remove all instances of 404.html
in the apps and place only one in project/templates/
folder instead. Use app/templates/app/
folders for app-specific templates only
I looked up the template again, but something is strange… What is the result of return render(“404.html”, status_code=404)
in artworks’ views.py?
Ok, this works:
try:
space = Space.objects.get(name=space_name)
group = Group.objects.get(name=group_name)
except:
return render(request, 'artwork/404.html')
I had been doing:
group = Group.objects.get_object_or_404(name=group_name)
but it produces:
"AttributeError: 'Manager' object has no attribute 'get_object_or_404'"
I couldn’t figure that one out, but I no longer care
The get_object_or_404
is a utility function, not a function on a model or manager.
In the example you show here, it should actually be:
group = get_object_or_404(Group, name=group_name)
See the docs at Django shortcut functions | Django documentation | Django
There’s an additional example in the tutorial at Writing your first Django app, part 4 | Django documentation | Django
Ok, I was tricked into thinking it was an object function because of the similarity to the get(…) function. RTFM!
But wouldn’t get_object_or_404 lead back to the original problem of Django using the first 404.html it happens to find? The try/catch version lets me specify the location.
That’s actually an intentional design decision and not a “problem” from Django’s perspective. Quoting directly from the docs for The Http404 exception:
For convenience, and because it’s a good idea to have a consistent 404 error page across your site, Django provides an Http404
exception.
So yes, handling the exeception yourself gives you the ability to tailor the 404 response by view, as you see fit, while the Django approach is to provide a single 404 page.
Got it. FWIW, it’s not necessarily a good design decision; in my case, one of the two apps is an an ‘admin’ site which has a different UX. Perhaps the function should take an optional location.