Template does not exist error

I accidentally deleted my templates from VS Code, and then recovered them from trash; and that’s when issues began.

The templates were in the app directory /ResilianceRadarR5/ISO22301/templates/ISO22301

after I recovered them by using Finder to move them, I had to move them to a template file folder in the root directory /ResilianceRadarR5/templates/ISO22301 in order for the template files to be found to be rendered.

I changed nothing else in urls.py or views.py; all I did was move the files from teh trash to their original location

In addition, it returns a 404 for logout.html when I try to access teh page from a button

urls.py

"""
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path("ISO22301/", include("ISO22301.urls")),
    path('accounts/', include('django.contrib.auth.urls')),
]

ISO 22301 urls.py

from django.urls import path
from django.contrib.auth import views as auth_views
from . import views

urlpatterns = [
    path("layout/", views.layout, name="layout"),
    path("survey/", views.survey, name="survey"),
    path("results/", views.results, name="results"),
    path('login/', views.user_login, name='login'),
    path('generic/', views.generic, name='generic'),
    path('introduction', views.introduction, name='introduction'),
    path('logout/', views.logout, name='logout'),
]

Relevant views.py

def user_login(request):
    if request.method == 'POST':
        form = LoginForm(request.POST)
        if form.is_valid():
            username = form.cleaned_data['username']
            password = form.cleaned_data['password']
            user = authenticate(request, username=username, password=password)
            if user is not None:
                login(request, user)    
                return redirect('introduction')
            else:
                return redirect('login')
    else:
        form = LoginForm()
    return render(request,'ISO22301/login.html', {'form': form},)

def logout(request):
    if request.user.is_authenticated:
        print("Logged in")
        logout(request)
        messages.success(request,('You are logged out'))
        return render(request,'ISO22301/logout.html',)
    else:
        print("Not logged in")

    #return redirect('logout')
    return render(request,'ISO22301/logout.html',)

Error Message for login.html

TemplateDoesNotExist at /ISO22301/login/
ISO22301/login.html
Request Method:	GET
Request URL:	http://127.0.0.1:8000/ISO22301/login/
Django Version:	5.1
Exception Type:	TemplateDoesNotExist
Exception Value:	
ISO22301/login.html
Exception Location:	/Users/beyondscorecard/Documents/Work/Python Projects/Dashboard/ResilianceRadarR5/.venv/lib/python3.12/site-packages/django/template/loader.py, line 19, in get_template
Raised during:	ISO22301.views.user_login
Python Executable:	/Users/beyondscorecard/Documents/Work/Python Projects/Dashboard/ResilianceRadarR5/.venv/bin/python
Python Version:	3.12.3
Python Path:	
['/Users/beyondscorecard/Documents/Work/Python '
 'Projects/Dashboard/ResilianceRadarR5',
 '/Library/Frameworks/Python.framework/Versions/3.12/lib/python312.zip',
 '/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12',
 '/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/lib-dynload',
 '/Users/beyondscorecard/Documents/Work/Python '
 'Projects/Dashboard/ResilianceRadarR5/.venv/lib/python3.12/site-packages']
Server time:	Mon, 16 Sep 2024 11:54:21 +0000
Template-loader postmortem

Django tried loading these templates, in this order:

Using engine django:
django.template.loaders.filesystem.Loader: /Users/beyondscorecard/Documents/Work/Python Projects/Dashboard/ResilianceRadarR5/templates/ISO22301/login.html (Source does not exist)
django.template.loaders.app_directories.Loader: /Users/beyondscorecard/Documents/Work/Python Projects/Dashboard/ResilianceRadarR5/.venv/lib/python3.12/site-packages/django/contrib/admin/templates/ISO22301/login.html (Source does not exist)
django.template.loaders.app_directories.Loader: /Users/beyondscorecard/Documents/Work/Python Projects/Dashboard/ResilianceRadarR5/.venv/lib/python3.12/site-packages/django/contrib/auth/templates/ISO22301/login.html (Source does not exist)
Using engine jinja2:
This engine did not provide a list of tried templates.

Code in used for logout

   <button input type='button' name=Logout onclick="location.href='ISO22301/logout.html'"><lable>Logout</lable></button>

Error message for logout

Page not found (404)
Request Method:	GET
Request URL:	http://127.0.0.1:8000/ISO22301/logout.html
Using the URLconf defined in ResilianceRadarR5.urls, Django tried these URL patterns, in this order:
admin/
ISO22301/ layout/ [name='layout']
ISO22301/ survey/ [name='survey']
ISO22301/ results/ [name='results']
ISO22301/ login/ [name='login']
ISO22301/ generic/ [name='generic']
ISO22301/ introduction [name='introduction']
ISO22301/ logout/ [name='logout']
accounts/
The current path, ISO22301/logout.html, 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.

You do not have a url named ISO22301/logout.html in your project. The url is ISO22301/logout/. (Yes, and the trailing slash is important here because that’s how you have it defined.)

As far as the template issues are concerned, we’d need to see your settings.py file and the output of an ls -R command from your project root, other than what’s in the .venv directory.

Thanks. It’s the simple errors that I beat my head against the wall for hours trying to fix…

I changed my Introduction url to match so I won’t add a / and then wonder why it doesn’t work in another button…

settings.py

DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
    'ISO22301',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'ResilianceRadarR5.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [ 'templates'],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
    {
        "BACKEND": "django.template.backends.jinja2.Jinja2",
        "DIRS": [
            "/home/html/jinja2",
        ],
    },
]

WSGI_APPLICATION = 'ResilianceRadarR5.wsgi.application'


# Database
# https://docs.djangoproject.com/en/5.0/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}


# Password validation
# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/5.0/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.0/howto/static-files/

STATIC_URL = 'static/'

STATIC_ROOT = os.path.join(BASE_DIR, 'assets')

# Default primary key field type
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

ls -R results from Terminal on Mac, not VS Code Terminal:

ISO22301		db.sqlite3		requirements.txt
ResilianceRadarR5	manage.py		templates
assets			requirements.in

./ISO22301:
ISO22301	admin.py	migrations	tests.py
__init__.py	apps.py		models.py	urls.py
__pycache__	forms.py	static		views.py

./ISO22301/ISO22301:
templates

./ISO22301/ISO22301/templates:
ISO22301

./ISO22301/ISO22301/templates/ISO22301:

./ISO22301/__pycache__:
__init__.cpython-312.pyc	models.cpython-312.pyc
admin.cpython-312.pyc		urls.cpython-312.pyc
apps.cpython-312.pyc		views.cpython-312.pyc
forms.cpython-312.pyc

./ISO22301/migrations:
0001_initial.py
0002_alter_topic_topic.py
0003_topic_area_alter_topic_topic.py
0004_area_header_area.py
0005_dashboard_project.py
0006_alter_project_outcome_text_and_more.py
0007_dashboard_company.py
0008_question_question_id.py
0009_alter_question_question_id.py
0010_rename_value_answer_q1_answer_q10_answer_q2_and_more.py
0011_respondent.py
0012_respondent_password_alter_respondent_company_and_more.py
0013_alter_respondent_password.py
0014_alter_answer_question.py
0015_rename_question_answer_respondent.py
0016_alter_comment_area.py
0017_rename_q1_answer_value_remove_answer_q10_and_more.py
0018_remove_question_question_id.py
0019_respondent_email_alter_area_header_areaheader_and_more.py
0020_question_area.py
0021_area_areatext.py
0022_area_topic.py
0023_answer_area.py
0024_rename_name_comment_respondent.py
0025_answer_company.py
0026_comment_company.py
__init__.py
__pycache__

./ISO22301/migrations/__pycache__:
0001_initial.cpython-312.pyc
0002_alter_topic_topic.cpython-312.pyc
0003_topic_area_alter_topic_topic.cpython-312.pyc
0004_area_header_area.cpython-312.pyc
0005_dashboard_project.cpython-312.pyc
0006_alter_project_outcome_text_and_more.cpython-312.pyc
0007_dashboard_company.cpython-312.pyc
0008_question_question_id.cpython-312.pyc
0009_alter_question_question_id.cpython-312.pyc
0010_rename_value_answer_q1_answer_q10_answer_q2_and_more.cpython-312.pyc
0011_respondent.cpython-312.pyc
0012_respondent_password_alter_respondent_company_and_more.cpython-312.pyc
0013_alter_respondent_password.cpython-312.pyc
0014_alter_answer_question.cpython-312.pyc
0015_rename_question_answer_respondent.cpython-312.pyc
0016_alter_comment_area.cpython-312.pyc
0017_rename_q1_answer_value_remove_answer_q10_and_more.cpython-312.pyc
0018_remove_question_question_id.cpython-312.pyc
0019_respondent_email_alter_area_header_areaheader_and_more.cpython-312.pyc
0020_question_area.cpython-312.pyc
0021_area_areatext.cpython-312.pyc
0022_area_topic.cpython-312.pyc
0023_answer_area.cpython-312.pyc
0024_rename_name_comment_respondent.cpython-312.pyc
0025_answer_company.cpython-312.pyc
0026_comment_company.cpython-312.pyc
__init__.cpython-312.pyc

./ISO22301/static:
css

./ISO22301/static/css:
styles.css

./ResilianceRadarR5:
__init__.py	asgi.py		urls.py
__pycache__	settings.py	wsgi.py

./ResilianceRadarR5/__pycache__:
__init__.cpython-312.pyc	urls.cpython-312.pyc
settings.cpython-312.pyc	wsgi.cpython-312.pyc

./assets:
admin	css

./assets/admin:
css	img	js

./assets/admin/css:
autocomplete.css		nav_sidebar.css
base.css			responsive.css
changelists.css			responsive_rtl.css
dark_mode.css			rtl.css
dashboard.css			unusable_password_field.css
forms.css			vendor
login.css			widgets.css

./assets/admin/css/vendor:
select2

./assets/admin/css/vendor/select2:
LICENSE-SELECT2.md	select2.css		select2.min.css

./assets/admin/img:
LICENSE			icon-clock.svg		inline-delete.svg
README.txt		icon-deletelink.svg	search.svg
calendar-icons.svg	icon-hidelink.svg	selector-icons.svg
gis			icon-no.svg		sorting-icons.svg
icon-addlink.svg	icon-unknown-alt.svg	tooltag-add.svg
icon-alert.svg		icon-unknown.svg	tooltag-arrowright.svg
icon-calendar.svg	icon-viewlink.svg
icon-changelink.svg	icon-yes.svg

./assets/admin/img/gis:
move_vertex_off.svg	move_vertex_on.svg

./assets/admin/js:
SelectBox.js			inlines.js
SelectFilter2.js		jquery.init.js
actions.js			nav_sidebar.js
admin				popup_response.js
autocomplete.js			prepopulate.js
calendar.js			prepopulate_init.js
cancel.js			theme.js
change_form.js			unusable_password_field.js
core.js				urlify.js
filters.js			vendor

./assets/admin/js/admin:
DateTimeShortcuts.js	RelatedObjectLookups.js

./assets/admin/js/vendor:
jquery	select2	xregexp

./assets/admin/js/vendor/jquery:
LICENSE.txt	jquery.js	jquery.min.js

./assets/admin/js/vendor/select2:
LICENSE.md		select2.full.js
i18n			select2.full.min.js

./assets/admin/js/vendor/select2/i18n:
af.js		en.js		hu.js		ms.js		sq.js
ar.js		es.js		hy.js		nb.js		sr-Cyrl.js
az.js		et.js		id.js		ne.js		sr.js
bg.js		eu.js		is.js		nl.js		sv.js
bn.js		fa.js		it.js		pl.js		th.js
bs.js		fi.js		ja.js		ps.js		tk.js
ca.js		fr.js		ka.js		pt-BR.js	tr.js
cs.js		gl.js		km.js		pt.js		uk.js
da.js		he.js		ko.js		ro.js		vi.js
de.js		hi.js		lt.js		ru.js		zh-CN.js
dsb.js		hr.js		lv.js		sk.js		zh-TW.js
el.js		hsb.js		mk.js		sl.js

./assets/admin/js/vendor/xregexp:
LICENSE.txt	xregexp.js	xregexp.min.js

./assets/css:
styles.css

./templates:
ISO22301

./templates/ISO22301:
GenericHTML.html	layout.html		noaccount.html
generic.html		login.html		results.html
introduction.html	logout.html		survey.html

Your templates directory is one level too deep. It should be under the ISO22301 directory and not in an ISO22301/ISO22301 directory. That’s why the rendering engine couldn’t find them.

Thanks. Fixed it. Much appreciated.

I added the / but it seems the button is appending the url to the url of the page that ha the button:

    <button input type='button' name=Logout onclick="location.href='ISO22301/logout/'"><lable>Logout</lable></button>

results in a url that generates a 404:

http://127.0.0.1:8000/ISO22301/survey/ISO22301/logout/

Oddly, if I go to http://127.0.0.1:8000/ISO22301/logout/ I get:

RecursionError at /ISO22301/logout/
maximum recursion depth exceeded
Request Method:	GET
Request URL:	http://127.0.0.1:8000/ISO22301/logout/
Django Version:	5.1
Exception Type:	RecursionError
Exception Value:	
maximum recursion depth exceeded
Exception Location:	/Users/beyondscorecard/Documents/Work/Python Projects/Dashboard/ResilianceRadarR5/.venv/lib/python3.12/site-packages/django/utils/functional.py, line 250, in inner
Raised during:	ISO22301.views.logout
Python Executable:	/Users/beyondscorecard/Documents/Work/Python Projects/Dashboard/ResilianceRadarR5/.venv/bin/python
Python Version:	3.12.3
Python Path:	
['/Users/beyondscorecard/Documents/Work/Python '
 'Projects/Dashboard/ResilianceRadarR5',
 '/Library/Frameworks/Python.framework/Versions/3.12/lib/python312.zip',
 '/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12',
 '/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/lib-dynload',
 '/Users/beyondscorecard/Documents/Work/Python '
 'Projects/Dashboard/ResilianceRadarR5/.venv/lib/python3.12/site-packages']
Server time:	Mon, 16 Sep 2024 13:27:56 +0000

Yes, that should be an absolute url - “/ISO22301/logout/”

In general terms there are a couple of things that should probably be improved.

Side note: As a general rule, it’s always more helpful if you post the exceptions with the stack trace that appear in the runserver console instead of the summary messages presented in the browser. I think we need to see that to try and address the recursion error. We’ll probably also need to see the logout.html template.

I thought I was but probably have a mix with just LoginForm - I’m off to learn more about LoginView and LogoutView and LoginForm / LogoutForm

Will do

Got it all working.

Login and logout should be using Django’s auth:

POST request instead of GET

<form action = '/ISO22301/logout/' method="POST" > 

views.py:

def user_login(request):
    if request.method == 'POST':
        form = LoginForm(request.POST)
        if form.is_valid():
            username = form.cleaned_data['username']
            password = form.cleaned_data['password']
            user = authenticate(request, username=username, password=password)
            if user is not None:
                login(request, user)   
                return redirect('introduction')
            else:
                return redirect('login')
    else:
        form = LoginForm()
    return render(request,'ISO22301/login.html', {'form': form},)

def user_logout(request):

    if request.method == 'POST':
        if request.user.is_authenticated:
            logout(request)
        return redirect('/logout/')
    #return render(request,'ISO22301/logout.html',)

Only issue is return redirect(‘introduction’) works like expected; while return redirect(‘/logout/’), redirect(‘/logout’), redirect(‘logout/’), redirect(‘logout’) all return a variant on the 404:

Using the URLconf defined in `ResilianceRadarR5.urls`, Django tried these URL patterns, in this order:

1. `admin/`
2. `ISO22301/`
3. `accounts/`

The current path, `logout`, didn’t match any of these.

urls.py:

from django.urls import path
from django.contrib.auth import views as auth_views
from . import views

urlpatterns = [
    path("layout/", views.layout, name="layout"),
    path("survey/", views.survey, name="survey"),
    path("results/", views.results, name="results"),
    path('login/', views.user_login, name='login'),
    path('generic/', views.generic, name='generic'),
    path('introduction/', views.introduction, name='introduction'),
    path('logout/', views.user_logout, name='logout'),

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path("ISO22301/", include("ISO22301.urls")),
    path('accounts/', include('django.contrib.auth.urls')),

Please confirm you’re showing two different urls.py files here?
One from the inner project directory and the other from your app directory?

Yes. Top apps, other inner

The version redirect('logout') should work. If that is failing, please post the complete error message and any traceback as shown in the runserver console.

It looks like it’s trying to append logout/ to the url in the form tag:

<form action = '/ISO22301/logout/' method="POST" > 

/ISO22301/logout/logout/

Here you are.


[16/Sep/2024 20:54:18] "GET /ISO22301/login/ HTTP/1.1" 200 738
[16/Sep/2024 20:54:27] "POST /ISO22301/login/ HTTP/1.1" 302 0
[16/Sep/2024 20:54:27] "GET /ISO22301/introduction/ HTTP/1.1" 200 3298
[16/Sep/2024 20:54:28] "GET /ISO22301/survey/ HTTP/1.1" 200 46768
[16/Sep/2024 20:54:49] "POST /ISO22301/survey/ HTTP/1.1" 200 2381
[16/Sep/2024 20:54:52] "POST /ISO22301/logout/ HTTP/1.1" 302 0
Not Found: /ISO22301/logout/logout/
[16/Sep/2024 20:54:52] "GET /ISO22301/logout/logout/ HTTP/1.1" 404 4269

and in Chrome:

Page not found (404)
Request Method:	GET
Request URL:	http://127.0.0.1:8000/ISO22301/logout/logout/
Using the URLconf defined in ResilianceRadarR5.urls, Django tried these URL patterns, in this order:

admin/
ISO22301/ layout/ [name='layout']
ISO22301/ survey/ [name='survey']
ISO22301/ results/ [name='results']
ISO22301/ login/ [name='login']
ISO22301/ generic/ [name='generic']
ISO22301/ introduction/ [name='introduction']
ISO22301/ logout/ [name='logout']
accounts/
The current path, ISO22301/logout/logout/, 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.

Please post your current logout view

def user_logout(request):
    if request.method == 'POST':
        if request.user.is_authenticated:
            logout(request)
        return redirect('logout/')

You still have a / in it.

Problem is when I remove the / I get:

Method Not Allowed (GET): /accounts/logout/
Method Not Allowed: /accounts/logout/
[16/Sep/2024 21:54:48] "GET /accounts/logout/ HTTP/1.1" 405 0

That’s because of what I posted earlier:

OK, I’m confused. I thought I was using POST by setting the form action, which sent post data to the user_logout view, which then logs out the user and then renders a page with text saying the user has logged out. When I look at the console, I see:

[16/Sep/2024 22:06:32] "GET /ISO22301/login/ HTTP/1.1" 200 738
[16/Sep/2024 22:06:38] "POST /ISO22301/login/ HTTP/1.1" 302 0
[16/Sep/2024 22:06:38] "GET /ISO22301/introduction/ HTTP/1.1" 200 3298
[16/Sep/2024 22:06:45] "GET /ISO22301/survey/ HTTP/1.1" 200 46768
[16/Sep/2024 22:07:05] "POST /ISO22301/survey/ HTTP/1.1" 200 2383
[16/Sep/2024 22:07:07] "POST /ISO22301/logout/ HTTP/1.1" 200 334

with

return render(request,'ISO22301/logout.html',)

Is it not using POST?

I can just use that, I am just confused why ‘introduction’ works and ‘logout’ doesn’t.

No, render returns an HttpResponse.

However, in your logout view, you’re not returning that. You’re returning a redirect, which because of your url structure, is redirecting you to the system logout page.

Thanks I appreciate your patience. is there a change I can make to my url structure to allow the redirect?