Добавьте функции аутентификации.

Здравствуйте, добро пожаловать во вторую часть этого учебника-курса, в котором вы изучите и поймете основы Django, создав клон популярного сайта Netflix.


Мы собираемся добавить функции аутентификации. По сути, мы позволим пользователям входить и регистрироваться.
Давайте приступим.

Добавьте шаблоны сайта

Для этого проекта мы не будем писать собственные файлы Html или CSS. Вместо этого мы воспользуемся работой, проделанной Карлосом Авила, которая доступна здесь: Netflix Clone. Спасибо ему.
Если вы откроете этот codepen, то увидите HTML-код и некоторые CSS.

Создайте папку templates

Django рекомендует помещать наши Html файлы или шаблоны в каталог templates в корне проекта. Затем внутри этой директории мы можем создать папку для каждого приложения проекта, чтобы разделить шаблоны каждого приложения. Это возможно благодаря строке: 'BACKEND': 'django.template.backends.django.DjangoTemplates', в файле настроек.
Кроме того, измените строку 58 файла настроек следующим образом:

  • создайте папку templates в корне проекта:mkir templates.
  • создайте в ней папку netflix для нашего приложения netflix:
    • cd templates
    • mkir netflix
  • создайте в ней файл index.html:
    • cd netflix

Django URLs

Помните, в первой части мы видели, что файл urls.py содержит все маршруты нашего проекта.
У нас есть только один маршрут для доступа к странице администратора.
Теперь мы добавим маршрут для главной страницы с помощью этой строки:
Изменим ее следующим образом:

from django.contrib import admin
from django.urls import path
from django.conf import settings
from django.conf.urls.static import static

from netflix.views import index_view # Add this line

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', index_view, name='home'), # Add this line
]
Войти в полноэкранный режим Выйти из полноэкранного режима

У нас должна возникнуть ошибка, потому что модуль index_view, импортированный строкой from netflix.views import index_view, еще не существует.
Это представление, которое будет выполняться, когда кто-то вызовет путь с именем home, который мы только что добавили в строке

Поэтому нам нужно создать index_view, который будет отображать домашнюю страницу для запрашивающих.

  • Откройте файл netflix/views.py и добавьте этот код:
def index_view(request, path):
    """Home page view."""
    return render(request, 'index.html')
Войти в полноэкранный режим Выйти из полноэкранного режима

Вот и все.

  • запустите проект: ‘python manage.py runserver’.
  • Откройте браузер и перейдите по адресу http://localhost:8000Вы должны увидеть что-то вроде этого:

Это означает, что мы можем получить доступ к нашей домашней странице. Но рендеринг не правильный, потому что мы еще не добавили стили.

Этим мы сейчас и займемся.

Статические файлы

В Django CSS, js и изображения, необходимые для рендеринга сайта, называются statics, поэтому Django рекомендует помещать их в папку statics.
Приступим:

  • Во-первых, создайте каталог static в корне проекта. Django будет искать статические файлы там, подобно тому, как Django находит шаблоны внутри templates: mkdir static.
  • Создайте папку для приложения netflix аналогично шаблонам:
    • cd static
    • mkdir netflix
  • создайте в ней файл style.css: touch style.css и скопируйте в него CSS часть кодепена.

Мы уже указали URL файлов статики с помощью строки:

Это означает, что мы должны получить к ним доступ следующим образом:

  • Добавьте эти строки в файл settings.py:
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),  # Add this line
]
Войти в полноэкранный режим Выйти из полноэкранного режима
  • Теперь добавим этот {% load static %} в начало файла index.html. Это добавляет тег static, который может быть использован для вставки ссылок из статических файлов. А затем используйте этот тег static для импорта нашего style.css.
  • Добавляет эту строку <link rel="stylesheet" href="{% static 'netflix/style.css' %}"> в тег <head> файла index.html.
  • Обновите главную страницу.

Вот и все.

Шаблоны регистрации и входа

Сначала мы добавим на главную страницу кнопки registration и login.

  • В файле index.html замените <a href="#">Account</a> на <a href="/register">Register</a><a href="/login">Login</a>.
    Нажатие на кнопку register должно перенаправить пользователя на маршрут register, который отобразит страницу регистрации, и таким же образом нажатие на кнопку login перенаправит пользователя на маршрут login, который отобразит страницу login.

  • Добавьте register_view и login_view в файл netflix/views.py:

def register_view(request):
    """Registration view."""
    return render(request, 'netflix/register.html')

def login_view(request):
    """Login view."""
    return render(request, 'netflix/login.html')
Вход в полноэкранный режим Выход из полноэкранного режима
  • Измените файл urls.py, чтобы добавить маршруты регистрации и входа:
...
from netflix.views import register_view # Add this line
from netflix.views import login_view # Add this line

urlpatterns = [
    ...
    path('register', register_view, name='register'), # Add this line
    path('login', login_view, name='login'), # Add this line
]
Войти в полноэкранный режим Выход из полноэкранного режима
  • Создайте файлы register.html и login.html внутри templates/netlfix/.Мы создадим регистрационный HTML, вдохновленный индексным файлом.
    • Для файла registration.html нужно просто скопировать и вставить следующий html-код:
{% load static %}
<html>
<head>
  <meta charset="utf-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>Netflix</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <script defer src="https://use.fontawesome.com/releases/v5.1.0/js/all.js"  crossorigin="anonymous"></script>
  <script src="main.js"></script>
  <link rel="stylesheet" href="{% static 'netflix/style.css' %}">
</head>
<body>
  <div class="wrapper">

    <!-- HEADER -->
    <header>
      <div class="netflixLogo">
        <a id="logo" href="/"><img src="https://github.com/carlosavilae/Netflix-Clone/blob/master/img/logo.PNG?raw=true" alt="Logo Image"></a>
      </div>      
      <nav class="main-nav">                
        <a href="/">Home</a>  
      </nav>
      <nav class="sub-nav">
        <a href="/login">Login</a>
      </nav>
    </header>
    <section class="main-container" >
      <div class="location" id="home">
          <div class="box">
          </div>
      </div>
    </section>
  </div>
</body>
</html>
Вход в полноэкранный режим Выход из полноэкранного режима
  • Для файла login.html нужно просто скопировать и вставить следующий html-код:
{% load static %}
<html>
<head>
  <meta charset="utf-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>Netflix</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <script defer src="https://use.fontawesome.com/releases/v5.1.0/js/all.js"  crossorigin="anonymous"></script>
  <script src="main.js"></script>
  <link rel="stylesheet" href="{% static 'netflix/style.css' %}">
</head>
<body>
  <div class="wrapper">

    <!-- HEADER -->
    <header>
      <div class="netflixLogo">
        <a id="logo" href="/"><img src="https://github.com/carlosavilae/Netflix-Clone/blob/master/img/logo.PNG?raw=true" alt="Logo Image"></a>
      </div>      
      <nav class="main-nav">                
        <a href="/">Home</a>  
      </nav>
      <nav class="sub-nav">
        <a href="/register">Register</a>
      </nav>
    </header>
    <section class="main-container" >
      <div class="location" id="home">
          <div class="box">
          </div>
      </div>
    </section>
  </div>
</body>
</html>
Вход в полноэкранный режим Выход из полноэкранного режима

Django Forms: Добавьте формы регистрации и входа

Django позволяет нам легко создавать и управлять формами.
Давайте создадим файл netlfix/forms.py для размещения форм, связанных с приложением Netflix:

  • внутри папки netlfix создайте файл forms.py: touch forms.py.
  • Добавьте в него этот код:
from django import forms
from django.contrib.auth.models import User


class RegisterForm(forms.Form):
    """Registration form class."""

    firstname = forms.CharField(label="First name")
    lastname = forms.CharField(label="Last name")
    email = forms.EmailField(label="Email Address")
    password = forms.CharField(label="Password", widget=forms.PasswordInput)
    password_conf = forms.CharField(label="Password confirmation", widget=forms.PasswordInput)


class LoginForm(forms.Form):
    """Login form class."""

    email = forms.EmailField(label="Email Address")
    password = forms.CharField(label="Password", widget=forms.PasswordInput)
Вход в полноэкранный режим Выйти из полноэкранного режима
  • Измените register_view следующим образом:
from .forms import RegisterForm
...
...
def register_view(request):
    """Registration view."""
    register_form = RegisterForm()
    return render(request, 'netflix/register.html', locals())
Войти в полноэкранный режим Выйти из полноэкранного режима

Обратите внимание, что аргумент locals(), переданный в render(), отправляет все локальные переменные в шаблон HTML (здесь register.html). Это означает, что мы можем получить доступ к нашей форме в register.html как к переменной с именем register_form. Эта функция настраивается.

  • В register.html измените тег <div class="box"> следующим образом:
<div class="box">
    <form id="registerForm" action="/register" method="POST">
        {% csrf_token %}
        {{ register_form.as_p }}
        <button type="submit">Register</button>
    </form>
</div>
Вход в полноэкранный режим Выйти из полноэкранного режима

Важное замечание: Вы заметите синтаксис {{ register_form.as_p }} и {% csrf_token %}. Помните, я говорил, что аргументы locals() отправляют все локальные переменные представления регистрации в шаблон, а переменная register_form отправляется в HTML шаблон (register.html). Затем для доступа к этим локальным переменным в register.html нам просто нужно обернуть имя переменной в {{ }}, как это сделано для {{ register_form.as_p }}. Теперь для {% csrf_token %} все немного иначе, поскольку он отображается не как переменная, а как Tag. Если вы хотите узнать больше, прочитайте эту часть документации. А {% csrf_token %} позволяет нам установить значение csrf_token пользовательской сессии для предотвращения CSRF-атак.

Обновите страницу регистрации.
Django автоматически сгенерирует HTML-код формы. Разве это не волшебство ^_^ ? И мы можем настроить его, но не в этом курсе.
Сейчас это должно выглядеть так:

  • Измените login_view следующим образом:
...
from .forms import LoginForm  # add this line
...
...
def login_view(request):
    """Login view."""
    login_form = LoginForm()
    return render(request, 'netflix/login.html', locals())
Вход в полноэкранный режим Выйти из полноэкранного режима
  • В login.html измените тег <div class="box"> следующим образом:
<div class="box">
    <form id="loginForm" action="/login" method="POST">
        {% csrf_token %}
        {{ login_form.as_p }}
        <button type="submit">Login</button>
    </form>
</div>
Вход в полноэкранный режим Выйти из полноэкранного режима

Обновите страницу входа в систему.
Теперь она должна выглядеть следующим образом:

Обработайте отправку форм.

Форма регистрации

Теперь, если пользователь заполнит форму и отправит ее, он должен иметь возможность зарегистрироваться.

Если вы посмотрите на HTML-код формы регистрации, то заметите, что значение свойства HTML формы action — это /register (маршрут регистрации в нашем url.py).
Мы изменим register_view для обработки отправки формы. (Но вы можете переместить пост в другое представление).

  • Измените register_view следующим образом:
from django.http import HttpResponseRedirect
from django.contrib.auth.models import User
from django.contrib.auth.hashers import make_password
...
...
def register_view(request):
    """Registration view."""
    if request.method == 'GET':
        # executed to render the registration page
        register_form = RegisterForm()
        return render(request, 'netflix/register.html', locals())
    else:
        # executed on registration form submission
        register_form = RegisterForm(request.POST)
        if register_form.is_valid():
            User.objects.create(
                first_name=request.POST.get('firstname'),
                last_name=request.POST.get('lastname'),
                email=request.POST.get('email'),
                username=request.POST.get('email'),
                password=make_password(request.POST.get('password'))
            )
            return HttpResponseRedirect('/login')
        return render(request, 'netflix/register.html', locals())
Войти в полноэкранный режим Выйти из полноэкранного режима

Теперь наш register_view обрабатывает как отображение страницы регистрации при условии if request.method == 'GET':, так и отправку формы регистрации при условии else. И, как я уже сказал, вам решать, как обрабатывать их с помощью двух разных представлений.
Давайте объясним, что мы делаем в условии else:

  • Если это так, то User.objects.create( выполняется для создания экземпляра модели User.
  • И return HttpResponseRedirect('/login') перенаправляет пользователя на login.page.
  • Если форма не действительна, то вместо этого выполняется условие else:.
  • Затем мы перенаправляем пользователя на страницу регистрации, и Django автоматически отобразит пользователю ошибки валидации, чтобы он мог исправить их и отправить форму снова. Это ли не волшебство ^_^.

Валидация регистрационной формы

У нас могут быть некоторые ограничения для наших данных.

  • Минимальная длина пароля равна 6, а максимальная — 20: внутри forms.py измените строку password = forms.CharField(label="Password", widget=forms.PasswordInput) на password = forms.CharField(label="Password", widget=forms.PasswordInput(render_value=True), min_length=6, max_length=20). (Вы заметили, что мы добавили render_value=True, это для того, чтобы сказать django сохранить значение пароля, когда форма возвращается обратно с ошибками).

  • подтверждение пароля должно совпадать с паролем, а email не должен существовать в базе данных. Чтобы обработать эти два ограничения валидации, мы переопределим поведение по умолчанию метода clean класса Django Form. Поэтому модифицируйте RegistrationForm следующим образом:

...
class RegisterForm(forms.Form):
    """Registration form class."""

    ....

    # Add this methods
    def clean(self):
        """Check if the form is validated."""
        # call the default `clean` method to perform 
        # default validation of the form (max_lenght and min_length defined for password for instance)
        super(RegisterForm, self).clean()

        # extract user input data
        email = self.cleaned_data.get('email')
        password = self.cleaned_data.get('password')
        password_conf = self.cleaned_data.get('password_conf')

        # Check if the password match the password confirmation
        if password != password_conf:
            self._errors['password_conf'] = self.error_class([
                "wrong confirmation"
            ])

        # Check if the email used doen't already exist
        if User.objects.filter(username=email).exists():
            self._errors['email'] = self.error_class(['Email already exist'])

        # return any errors if found
        return self.cleaned_data
Войти в полноэкранный режим Выйти из полноэкранного режима

Метод достаточно документирован, чтобы объяснить, что он делает.
Вот и все.

Примечание: Для валидаторов паролей вы можете использовать настройки AUTH_PASSWORD_VALIDATORS, использовать валидаторы Django по умолчанию или создать свои собственные. Но в этом руководстве мы будем использовать вместо этого Django форму. Дайте мне знать в комментариях, если вам нужен учебник по созданию собственных валидаторов паролей.

Попробуйте зарегистрировать пользователя с неправильным вводом, а затем с правильным.
После того, как вы закончите. Войдите в django admin с правами суперпользователя и перейдите в http://localhost:8000/admin/auth/user/. Вы увидите, что новый пользователь добавлен. Вы также можете проверить в оболочке django shell:

from django.contrib.auth.models import User
User.objects.all()
Войти в полноэкранный режим Выйдите из полноэкранного режима

. Вы должны увидеть нового пользователя.

Форма входа в систему

Теперь зарегистрированный пользователь должен иметь возможность войти в систему.

Если вы посмотрите на HTML-код формы входа, то заметите, что значение свойства HTML формы action — это /login (маршрут входа в нашем url.py).
Мы изменим login_view для обработки отправки формы.

  • Измените login_view следующим образом:
...
from django.contrib.auth import authenticate, login  # Add this line

from .forms import LoginForm # Add this line
...
...
def login_view(request):
    """Login view."""
    if request.method == 'GET':
        # executed to render the login page
        login_form = LoginForm()
        return render(request, 'netflix/login.html', locals())
    else:
        # get user credentials input
        username = request.POST['email']
        password = request.POST['password']
        # If the email provided by user exists and match the
        # password he provided, then we authenticate him.
        user = authenticate(username=username, password=password)
        if user is not None:
            # if the credentials are good, we login the user
            login(request, user)
            # then we redirect him to home page
            return HttpResponseRedirect('/')
        # if the credentials are wrong, we redirect him to login and let him know
        return render(
            request,
            'netflix/login.html',
            {
                'wrong_credentials': True,
                'login_form': LoginForm(request.POST)
            }
        )
Войти в полноэкранный режим Выйти из полноэкранного режима

Этот метод достаточно документирован.

  • Измените файл login.html следующим образом:
<form id="loginForm" action="/login" method="POST">
    {% csrf_token %}
    {{ login_form.as_p }}
    <button type="submit">Login</button>
</form>
Войти в полноэкранный режим Выйти из полноэкранного режима

в этот:

<form id="loginForm" action="/login" method="POST">
    {% csrf_token %}
    {% if wrong_credentials %}
        <p style="margin-top: 60px">Invalid credentials.</p>
    {% endif %}
    {{ login_form.as_p }}
    <button type="submit">Login</button>
</form>
Войти в полноэкранный режим Выход из полноэкранного режима

Обратите внимание, что мы только что добавили:

{% if wrong_credentials %}
    <p style="margin-top: 60px">Invalid credentials.</p>
{% endif %}
Войти в полноэкранный режим Выйти из полноэкранного режима

Важное замечание: Это называется шаблонный условный рендеринг. Django позволяет нам отображать HTML в соответствии с некоторыми условиями. Так, здесь мы выводим сообщение Invalid credentials, если представление возвращает переменную wrong_credentials со значением True.

  • Теперь, когда пользователь будет аутентифицирован и перенаправлен на главную страницу, мы отобразим его имя пользователя и кнопку выхода из системы вместо кнопок login и register. Измените строку
<a href="/register">Register</a><a href="/login">Login</a> 
Войти в полноэкранный режим Выйти из полноэкранного режима

из файла index.html в эти:

{% if request.user.is_authenticated %}
  {{ request.user }}
  <a href="/logout"><i class="fas fa-power-off sub-nav-logo"></i>Logout</a>
{% else %}
  <a href="/register">Register</a><a href="/login">Login</a>
{% endif %}
Enter fullscreen mode Выйти из полноэкранного режима

Примечание: Здесь мы снова использовали условный рендеринг для достижения нашей цели с помощью {% if request.user.is_authenticated %}. Но вы можете спросить, что мы не отправили эту переменную (request) в HTML-шаблон. И вы правы. Мы можем получить доступ к переменной request, потому что это глобальная (контекстная) переменная, автоматически отправляемая Django, чтобы позволить нам получить полезную информацию о текущем запросе. Так, если {{ request.user.is_authenticated }} равно True, пользователь аутентифицирован, а request.user является экземпляром модели User аутентифицированного пользователя. И тогда мы можем отобразить email пользователя, используя request.user.username или непосредственно request.user. Но если вместо этого {{ request.user.is_authenticated }} будет False, мы отобразим кнопки регистрации и входа.

Если вы вошли в систему с правильными учетными данными, вы должны увидеть электронную почту аутентифицированного пользователя и кнопку выхода из системы, как показано здесь:

Выход из системы

Аутентифицированный пользователь должен иметь возможность выйти из системы.
У нас уже есть кнопка выхода из системы в файле index.html:

<a href="/logout"><i class="fas fa-power-off sub-nav-logo"></i>Logout</a>
Войти в полноэкранный режим Выйти из полноэкранного режима

Обратите внимание, что ссылка указывает на маршрут logout, который еще не существует.
Давайте добавим его.

  • В нижней части файла views.py добавьте logout_view:
...
...
def logout_view(request):
    """Logout view."""
    # logout the request
    logout(request)
    # redirect user to home page
    return HttpResponseRedirect('/')
Войти в полноэкранный режим Выход из полноэкранного режима
  • Добавьте представление выхода из системы в файл urls.py:
...
...
from netflix.views import logout_view # Add this line

urlpatterns = [
    ...
    ...
    path('logout', logout_view, name='logout'), # Add this line
]
Войти в полноэкранный режим Выход из полноэкранного режима

Поздравляем.

Мы подошли к концу этой второй части.

Резюме

В этом руководстве вы узнали

  • как управлять шаблоном Django
  • как выводить переменные внутри шаблона
  • как условно выводить HTML внутри шаблона
  • как управлять статическими файлами Django
  • как создавать формы Django и обрабатывать валидацию
  • как получить доступ и использовать переменную request в шаблоне Django
  • как обрабатывать базовую аутентификацию в Django (регистрация, вход и выход).

NB: Полный исходный код доступен здесь: https://github.com/fayomihorace/django-netflix-clone.

Если вы столкнулись с какой-либо блокировкой или ошибкой при выполнении этого руководства, пожалуйста, оставьте комментарий, я отвечу и помогу вам как можно скорее. С нетерпением жду вас в третьей части этого учебника-курса.

Оцените статью
Procodings.ru
Добавить комментарий