Форма аутентификации и авторизации Django в Python
Рассмотрим, как создать форму аутентификации и авторизацииDjango в Python, которая позволяет пользователям входить в систему, используя имя пользователя и пароль.
- Создание нового приложения
- Создание формы входа
- Добавление формы выхода
- Скрытие ссылок редактирования и удаления в списке постов
- Защита защищенных страниц
Создание нового приложения
Сначала создайте новое приложение с именем users, выполнив команду startapp:
django-admin startapp users
Каталог проекта будет выглядеть так:
├── blog ├── db.sqlite3 ├── manage.py ├── mysite ├── static ├── templates └── users
Затем зарегистрируйте приложение пользователя в установленных приложениях settings.py проекта:
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', # local 'blog.apps.BlogConfig', 'users.apps.UsersConfig' ]
Создайте новый файл urls.py внутри приложения пользователя со следующим кодом:
from django.urls import path from . import views urlpatterns = []
Наконец, включите urls.py приложения пользователя в urls.py проекта с помощью функции include():
from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('',include('blog.urls')), path('',include('users.urls')) ]
Создание формы входа
- Сначала создайте URL-адрес для входа в urls.py приложения пользователя:
from django.urls import path from . import views urlpatterns = [ path('login/', views.sign_in, name='login'), ]
- Во-вторых, создайте файл forms.py в приложении пользователя и определите LoginForm, который наследуется от класса Form:
from django import forms class LoginForm(forms.Form): username = forms.CharField(max_length=65) password = forms.CharField(max_length=65, widget=forms.PasswordInput)
LoginForm имеет два поля: имя пользователя и пароль.
- В-третьих, создайте функцию sign_in() в файле views.py приложения пользователя для визуализации шаблона login.html:
from django.shortcuts import render from .forms import LoginForm def sign_in(request): if request.method == 'GET': form = LoginForm() return render(request, 'users/login.html', {'form': form})
- В-четвертых, создайте каталог templates/users внутри приложения users:
mkdir templates cd templates mkdir users
- В-пятых, создайте шаблон login.html внутри каталога templates/users, который расширяет шаблон base.html:
{% extends 'base.html' %} {% block content %} <form method="POST" novalidate> {% csrf_token %} <h2>Login</h2> {{form.as_p}} <input type="submit" value="Login" /> </form> {% endblock content%}
- В-шестых, откройте URL-адрес входа, и вы увидите форму входа:
http://127.0.0.1:8000/login
Если вы введете имя пользователя/пароль и нажмете кнопку «Войти», вы получите сообщение об ошибке, поскольку мы еще не добавили код, обрабатывающий HTTP-запрос POST.
- В-седьмых, измените функцию sign_in() для обработки процесса входа в систему:
from django.shortcuts import render, redirect from django.contrib import messages from django.contrib.auth import login, authenticate from .forms import LoginForm def sign_in(request): if request.method == 'GET': form = LoginForm() return render(request,'users/login.html', {'form': form}) elif 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: login(request, user) messages.success(request,f'Hi {username.title()}, welcome back!') return redirect('posts') # form is not valid or user is not authenticated messages.error(request,f'Invalid username or password') return render(request,'users/login.html',{'form': form})
Как это работает.
Сначала импортируйте функцию аутентификации и входа из модуля django.contrib.auth:
from django.contrib.auth import login, authenticate
Функция authenticate() проверяет имя пользователя и пароль. Если имя пользователя и пароль действительны, она возвращает экземпляр класса User или None в противном случае.
Функция login() регистрирует пользователя. Технически она создает идентификатор сеанса на сервере и отправляет его обратно в веб-браузер в виде файла cookie.
В последующем запросе веб-браузер отправляет идентификатор сеанса обратно на веб-сервер, Django сопоставляет значение cookie с идентификатором сеанса и создает объект User.
Во-вторых, проверьте имя пользователя и пароль с помощью функции authenticate(), если форма действительна:
user = authenticate(request, username=username, password=password)
В-третьих, авторизуйте пользователя, создайте флэш-сообщение и перенаправьте пользователя на URL-адрес публикации, если имя пользователя и пароль верны:
if user: login(request, user) messages.success(request,f'Hi {user.username.title()}, welcome back!') return redirect('posts')
В противном случае создайте флэш-сообщение об ошибке и перенаправьте пользователя обратно на страницу входа:
messages.error(request,f'Invalid username or password') return render(request,'users/login.html')
Если вы введете имя пользователя без пароля и нажмете кнопку «Войти», вы получите следующее сообщение об ошибке:
Однако если вы введете правильное имя пользователя/пароль, вы будете перенаправлены на страницу списка публикаций с приветственным сообщением:
Добавление формы выхода
Сначала определите маршрут для выхода пользователя из системы:
from django.urls import path from . import views urlpatterns = [ path('login/', views.sign_in, name='login'), path('logout/', views.sign_out, name='logout'), ]
Во-вторых, определите функцию sign_out() в файле views.py, которая обрабатывает маршрут выхода из системы:
from django.shortcuts import render, redirect from django.contrib import messages from django.contrib.auth import login, authenticate, logout from .forms import LoginForm def sign_in(request): if request.method == 'GET': form = LoginForm() return render(request,'users/login.html', {'form': form}) elif 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: login(request, user) messages.success(request,f'Hi {username.title()}, welcome back!') return redirect('posts') # either form not valid or user is not authenticated messages.error(request,f'Invalid username or password') return render(request,'users/login.html',{'form': form}) def sign_out(request): logout(request) messages.success(request,f'You have been logged out.') return redirect('login')
Если вы войдете в систему и зайдете на страницу входа, вы все равно увидите форму входа. Поэтому лучше перенаправить вошедшего в систему пользователя на список сообщений, если пользователь зайдет на страницу входа.
В-третьих, измените функцию sign_in() в файле views.py приложения пользователя:
def sign_in(request): if request.method == 'GET': if request.user.is_authenticated: return redirect('posts') form = LoginForm() return render(request,'users/login.html', {'form': form}) elif 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: login(request, user) messages.success(request,f'Hi {username.title()}, welcome back!') return redirect('posts') # either form not valid or user is not authenticated messages.error(request,f'Invalid username or password') return render(request,'users/login.html',{'form': form})
Функция request.user.is_authenticated возвращает True, если пользователь вошел в систему, или False в противном случае.
В-четвертых, измените шаблон base.html, включив в него ссылку для выхода из системы, если пользователь аутентифицирован, и ссылку для входа в противном случае:
{%load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" href="{% static 'css/style.css' %}" /> <script src="{% static 'js/app.js' %}" defer></script> <title>My Site</title> </head> <body> <header> {%if request.user.is_authenticated %} <span>Hi {{ request.user.username | title }}</span> <a href="{% url 'logout' %}">Logout</a> {%else%} <a href="{% url 'login' %}">Login</a> {%endif%} </header> <main> {% if messages %} <div class="messages"> {% for message in messages %} <div class="alert {% if message.tags %}alert-{{ message.tags }}"{% endif %}> {{ message }} </div> {% endfor %} </div> {% endif %} {%block content%} {%endblock content%} </main> </body> </html>
Если вы зайдете на сайт, вы увидите ссылку для входа:
При нажатии на ссылку входа откроется страница входа:
Если вы введете действительные имя пользователя и пароль и войдете в систему, вы увидите приветственное сообщение, а также ссылку для выхода из системы:
Если нажать на ссылку выхода, произойдет перенаправление на страницу входа:
Скрытие ссылок редактирования и удаления в списке постов
Если пользователь вошел в систему, request.user.is_authenticated имеет значение True. Таким образом, вы можете использовать этот объект для отображения и скрытия элементов страницы, независимо от того, вошел пользователь в систему или нет.
Например, следующий код скрывает ссылки редактирования и удаления в шаблоне blog/home.html, если пользователь аутентифицирован:
{% extends 'base.html' %} {% block content %} <h1>My Posts</h1> {% for post in posts %} <h2>{{ post.title }}</h2> <small>Published on {{ post.published_at | date:"M d, Y" }} by {{ post.author | title}}</small> <p>{{ post.content }}</p> {% if request.user.is_authenticated %} <p> <a href="{% url 'post-edit' post.id %}">Edit</a> <a href="{% url 'post-delete' post.id%}">Delete</a> </p> {% endif %} {% endfor %} {% endblock content %}
Защита защищенных страниц
Обычно следует разрешить аутентифицированным пользователям доступ к созданию, обновлению и удалению страниц постов. Для этого можно использовать декоратор login_required из Django.
Если функция представления имеет декоратор login_required и неаутентифицированный пользователь попытается запустить ее, Django перенаправит пользователя на страницу входа.
Мы защитим функции создания, обновления и удаления записей с помощью декоратора login_required.
Сначала установите URL-адрес входа в settings.py следующим образом:
LOGIN_URL = 'login'
Если этого не сделать, Django выполнит перенаправление на URL-адрес входа по умолчанию, который будет accounts/login/, а не users/login, как мы определили в этом проекте.
Во-вторых, измените views.py приложения блога, добавив декоратор @login_required к функциям create_post, edit_post и delete_post:
from django.shortcuts import render, redirect, get_object_or_404 from django.contrib import messages from django.contrib.auth.decorators import login_required from .models import Post from .forms import PostForm @login_required def delete_post(request, id): post = get_object_or_404(Post, pk=id) context = {'post': post} if request.method == 'GET': return render(request, 'blog/post_confirm_delete.html', context) elif request.method == 'POST': post.delete() messages.success(request, 'The post has been deleted successfully.') return redirect('posts') @login_required def edit_post(request, id): post = get_object_or_404(Post, id=id) if request.method == 'GET': context = {'form': PostForm(instance=post), 'id': id} return render(request, 'blog/post_form.html', context) elif request.method == 'POST': form = PostForm(request.POST, instance=post) if form.is_valid(): form.save() messages.success( request, 'The post has been updated successfully.') return redirect('posts') else: messages.error(request, 'Please correct the following errors:') return render(request, 'blog/post_form.html', {'form': form}) @login_required def create_post(request): if request.method == 'GET': context = {'form': PostForm()} return render(request, 'blog/post_form.html', context) elif request.method == 'POST': form = PostForm(request.POST) if form.is_valid(): form.save() messages.success( request, 'The post has been created successfully.') return redirect('posts') else: messages.error(request, 'Please correct the following errors:') return render(request, 'blog/post_form.html', {'form': form}) def home(request): posts = Post.objects.all() context = {'posts': posts} return render(request, 'blog/home.html', context) def about(request): return render(request, 'blog/about.html')
Если вы откроете URL-адрес создания, обновления или удаления, например:
http://127.0.0.1/post/create
Он будет перенаправлен на страницу входа.