I'm following a tutorial to build a To Do list in Python and Django, I've finished implementing authentication and User-based access using built-in views,
Upon clicking the "logout" button I'm correctly redirected to the login page. However, if I type http://localhost:8000/logout directly in the URL it shows a 405 error. My goal is to redirect the user if someone directly types the URL to the login page. How do I do that?
Below is my url.py file
from django.urls import path, reverse_lazy
#from . import views # For Functions, thus but now it's class so
from .views import TaskList, TaskDetail, TaskCreate, TaskUpdate, TaskDelete, CustomLoginView, RegisterPage # CLASSES FROM VIEWS FILES
from django.contrib.auth.views import LogoutView
urlpatterns = [
path('login/',CustomLoginView.as_view(),name='login'),
path('logout/',LogoutView.as_view(next_page=reverse_lazy('login')),name='logout'),
path('register/', RegisterPage.as_view(),name="register"),
path('', TaskList.as_view(), name='tasks'), # root URL - #base url thus empty string, view name
path('task/<int:pk>', TaskDetail.as_view(), name='task'), # when clicked on list item - sub url <int:pk> - pk - primary key
path('create-task/', TaskCreate.as_view(), name='task-create'), # url accessed to create the task
path('update-task/<int:pk>', TaskUpdate.as_view(), name='task-update'), # url accessed to update the task
path('delete-task/<int:pk>', TaskDelete.as_view(), name='task-delete'), # url accessed to delete the task
]
Below are my relevent view.py functions
from django.shortcuts import render, redirect
from django.http import HttpResponse
from django.views.generic.list import ListView
from django.views.generic.detail import DetailView #description pane
from django.views.generic.edit import CreateView, UpdateView, DeleteView, FormView # create form; update prefill and modify the data
from django.urls import reverse_lazy # redirects user to certain part or page
from django.contrib.auth.views import LoginView
from django.contrib.auth.mixins import LoginRequiredMixin #add this before builtin view to prevent unauthorised user accessing data,
from django.contrib.auth.forms import UserCreationForm #built in user creation
from django.contrib.auth import login #to login user automatically once registered
from .models import Task
# Create your views here.
#if not logged in then restrict people from seeing it
#gatekeeper thus on top - login optionality
class CustomLoginView(LoginView):
template_name = 'base/login.html'
fields = '__all__'
redirect_authenticated_user = True #--- unauthenticated users to be redirected
def get_success_url(self):
return reverse_lazy('tasks')
class RegisterPage(FormView):
template_name = 'base/register.html'
form_class = UserCreationForm #--- passing prebuilt form
redirect_authenticated_user = True
success_url = reverse_lazy('tasks') #--- redirect on success when registered
def form_valid(self, form): #-- the form submission is valid by rules
user = form.save() #-- save the user
if user is not None: #-- is user is suceesfully created
login(self.request, user) #-- redirect control to login function for autologin
return super().form_valid(form)
def get(self,*args, **kwargs):
if self.request.user.is_authenticated:
return redirect('tasks')
return super().get(*args,**kwargs)
Below is where the logout button is placed in the whole application
task_list.html
<!-- homepage tasks listing -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>To Do</title>
</head>
<body>
{% if request.user.is_authenticated %}
<p>{{ request.user }}</p>
<!-- <a href="{% url 'logout' %}">Log Out</a> Won't work error 405, need POst and csrf token by django -->
<form action="{% url 'logout' %}" method="post">
{% csrf_token %}
<button type="submit">Logout</button>
</form>
{% else %}
<a href="{% url 'login' %}">Log In</a>
{% endif %}
<hr>
<h1>My To Do List</h1>
<a href="{% url 'task-create' %}"> Add Items</a>
<table>
<tr>
<th>Items</th> <!-- th acts as cell header -->
<th> </th> <!-- #td only shows if we've a th for each td-->
<th> </th>
<th> </th>
</tr>
<!-- loop through all the items -->
{% for task in tasks %} <!-- this si django templating syntax-->
<tr>
<td>{{task.title}}</td> <!-- td acts as cell value -->
<td><a href="{% url 'task' task.id %}">View</a></td>
<td><a href="{% url 'task-update' task.id %}">Edit</a></td>
<td><a href="{% url 'task-delete' task.id %}">Delete</a></td>
</tr>
{% empty %}
<h3>No Items in List</h3>
{% endfor %}
</table>
</body>
</html>
I've tried searching but everywhere only the below solution is mentioned and only for a dedicated button. no solution on how to handle the localhot:8000/logout URL
<form action="{% url 'logout' %}" method="post">
{% csrf_token %}
<button type="submit">Logout</button>
</form>
based on the Django's source code for LogoutView
class LogoutView(RedirectURLMixin, TemplateView):
"""
Log out the user and display the 'You are logged out' message.
"""
http_method_names = ["post", "options"]
template_name = "registration/logged_out.html"
extra_context = None
@method_decorator(csrf_protect)
@method_decorator(never_cache)
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
"""Logout may be done via POST."""
auth_logout(request)
redirect_to = self.get_success_url()
if redirect_to != request.get_full_path():
# Redirect to target page once the session has been cleared.
return HttpResponseRedirect(redirect_to)
return super().get(request, *args, **kwargs)
linked here: https://github.com/django/django/blob/main/django/contrib/auth/views.py
This view will not handle GET requests and when you call it, will return 405 Method Not Allowed.
So if you really need to do this, you have to write your own Logout view.