В качестве хобби, я решил открыть для себя мир веб-разработки, попутно освещая свои успехи, а может и неудачи на пути к просветлению…

В настоящее время я публично описываю процесс создания веб-сайта с нуля на Django и Bootstrap, где задача разработать такие приложения как: аккаунты, вопросы, статьи и книги.

В проекте решаются только реальные задачи, которые почему-то так любят обходить стороной в учебной литературе.

Создание веб-сайта с нуля на Django и Bootstrap. Аккаунты. Процедуры входа и выхода

Сергей Серов

В Django присутствуют низкоуровневые средства для аутентификации и авторизации пользователей.

Все что нам необходимо — это внедрить данный функционал: создав форму для входа и несколько представлений.

Форма

Приступаем к созданию формы, открываем в каталоге приложения accounts файл forms.py и добавляем следующие строки:

from django.contrib.auth import authenticate, login


class AuthForm(forms.Form):
    email = forms.EmailField(label='Адрес электронной почты', max_length=254,
                             widget=forms.EmailInput(attrs={'autofocus': ''}))
    password = forms.CharField(label='Пароль', max_length=255, strip=False, widget=forms.PasswordInput)

    def __init__(self, *args, **kwargs):
        super(AuthForm, self).__init__(*args, **kwargs)
        self.user = None

    def clean(self):
        email = self.cleaned_data.get('email')
        password = self.cleaned_data.get('password')
        if email and password:
            self.user = authenticate(email=email, password=password)
            if not self.user:
                raise forms.ValidationError('Введены неправильные учетные данные или пользователь неактивен')
        return self.cleaned_data

    def login(self, request):
        login(request, self.user)

Представления

Далее открываем файл views.py и добавляем следующее:

from django.contrib.auth import logout


@anonymous_required
def login_view(request):
    form = forms.AuthForm(request.POST or None)
    if form.is_valid():
        form.login(request)
        return redirect('index')
    return render(request, 'accounts/login.html', {
        'form': form,
    })


def logout_view(request):
    logout(request)
    return redirect('accounts:login')

В login_view мы отображаем форму аутентификации и в случае правильного заполнения проводим авторизацию пользователя и затем перенаправляем на главную станицу. В случае если аутентификация прошла по каким-либо причинам не успешно — отображаем шаблон с подсказками как исправить ситуацию.

В свою очередь logout_view не использует каких-либо дополнительных проверок или действий и просто запускает штатную процедура выхода и затем перенаправляет на страницу входа.

Маршруты

Открываем urls.py и приводим файл к следующему виду:

from django.conf.urls import url

from . import views

app_name = 'accounts'
urlpatterns = [
    url(r'^join/$', views.join_view, name='join'),
    url(r'^join/confirm/(?:(?P<code>\w+)/)?$', views.join_confirm_view, name='join_confirm'),
    url(r'^join/resend/$', views.join_resend_view, name='join_resend'),
    url(r'^login/$', views.login_view, name='login'),
    url(r'^logout/$', views.logout_view, name='logout'),
]

Шаблоны

Переходим к заключительному шагу — создание шаблонов.

В директории templates выбираем подкаталог accounts и создаем файл login.html:

{% extends 'base.html' %}

{% block title %}Войти{% endblock %}

{% block main %}
    <div class="accounts-login">
        <h1>Войти</h1>

        <form action="{% url 'accounts:login' %}" method="post">
            {% csrf_token %}
            {% include 'core/form_fields.html' %}
            <button type="submit" class="btn btn-success">Войти</button>
        </form>

        <div class="links">
            <p><a href="{% url 'accounts:join' %}">Присоединиться к нам!</a></p>
            {% comment %}
            <p><a href="{% url 'accounts:change_password' %}">Забыли пароль?</a></p>
            {% endcomment %}
        </div>
    </div>
{% endblock %}

Далее нам необходимо поправить базовый шаблон для внедрения ссылок манипуляций с аккаунтом. В директории templates открываем файл base.html и заменяем следующим содержимым:

{% load static %}
<!DOCTYPE html>
<html lang="ru">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <meta name="author" content="{{ SITE_AUTHOR }}">

        <title>{% block title %}{% endblock %} | {{ SITE_TITLE }}</title>

        <!-- Bootstrap -->
        <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
        <link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">

        <!-- Style -->
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
        <link href="https://fonts.googleapis.com/css?family=Open+Sans:400,700%7CUbuntu:700&amp;subset=cyrillic" rel="stylesheet">
        <link rel="stylesheet" href="{% static 'style.css' %}">

        <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
        <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
        <!--[if lt IE 9]>
            <script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
            <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
        <![endif]-->

        <!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
        <!-- Include all compiled plugins (below), or include individual files as needed -->
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
    </head>
    <body>
        <header>
            <nav class="navbar navbar-default navbar-static-top">
                <div class="container">
                    <ul class="nav navbar-nav navbar-right">
                        {% if user.is_authenticated %}
                            <li class="dropdown">
                                <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
                                    <i class="fa fa-user fa-fw" aria-hidden="true"></i>
                                    {{ user.username }}
                                    <span class="caret"></span>
                                </a>
                                <ul class="dropdown-menu">
                                    {% comment %}
                                    <li>
                                        <a href="{% url 'accounts:change_password' %}">
                                            <i class="fa fa-key fa-fw" aria-hidden="true"></i>
                                            Смена пароля
                                        </a>
                                    </li>

                                    <li>
                                        <a href="{% url 'accounts:change_email' %}">
                                            <i class="fa fa-envelope-o fa-fw" aria-hidden="true"></i>
                                            Смена почты
                                        </a>
                                    </li>
                                    {% endcomment %}

                                    <li><a href="{% url 'accounts:logout' %}">
                                        <i class="fa fa-sign-out fa-fw" aria-hidden="true"></i>
                                        Выход
                                    </a></li>
                                </ul>
                            </li>
                        {% else %}
                            <li>
                                <a href="{% url 'accounts:login' %}">
                                    <i class="fa fa-sign-in fa-fw" aria-hidden="true"></i>
                                    Войти
                                </a>
                            </li>
                        {% endif %}
                    </ul>
                </div>
            </nav>

            <div class="container">
                <div class="about">
                    <div class="row">
                        <div class="col-sm-8">
                            <h2 class="title"><a href="/">{{ SITE_TITLE }}</a></h2>
                            <h3 class="subtitle">{{ SITE_SUBTITLE }}</h3>
                        </div>

                        <div class="col-sm-4">
                            <address class="contacts">
                                <div class="mail">
                                    <span class="sr-only">Адрес электронной почты</span>
                                    <i class="fa fa-envelope-o fa-fw" aria-hidden="true"></i>
                                    Почта
                                </div>

                                <div class="location">
                                    <span class="sr-only">Местоположение</span>
                                    <i class="fa fa-globe fa-fw" aria-hidden="true"></i>
                                    Местоположение
                                </div>
                            </address>
                        </div>
                    </div>

                    <div class="description">
                        <p>
                            В течение десяти лет работы на крупных производственных предприятиях заполучил опыт создания служб с нуля,
                            по направлениям: охрана труда, пожарная, промышленная и экологическая безопасность.
                        </p>

                        <p>
                            Проведу для Вас полный аудит или разработаю комплекты документов по всем вышеперечисленным направлениям.
                        </p>
                    </div>
                </div>
            </div>
        </header>

        <main>
            <div class="container">
                {% block main %}{% endblock %}
            </div>
        </main>
    </body>
</html>

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