Пишем web на Python3, верстаем страницу входа в сервис

newbie

Опубликован:  2019-03-05T10:40:32.878119Z
Отредактирован:  2019-03-16T09:50:08.518679Z

Продолжаем работу над проектом selfish. Этот обзор посвящен вёрстке страницы входа в сервис, я покажу ключевые особенности этого процесса, сверстаю страницу входа в сервис и протестирую разработанную в предыдущем выпуске блога логику входа пользователя в сервис в браузере в ручном режиме.

На текущем этапе разработки мне необходимо сверстать шаблон, при помощи которого функция представления auth.login будет рендерить соответствующую строку в HTTP-ответ сервера. Шаблон страницы - это место, где backend пересекается с frontend, и, разрабатывая шаблон, web-программист имеет дело и с серверной и с клиентской частью приложения.

Прежде чем приступить к вёрстке целевого шаблона, следует выполнить некоторые подготовительные действия. Для начала я проведу небольшой рефакторинг JavaScript-сценариев. Меня интересует существующий файл selfish/static/js/main/index.js, открываю его в редакторе.

obEQ9abaGs.png

Дело в том, что выделенная на снимке экрана выше функция showDateTime мне понадобится чуть позже, и поэтому её нужно изъять в отдельный файл, который будет содержать только эту функцию. Создаю новый файл.

touch selfish/static/js/show-datetime.js

Открываю это файл в редакторе и копирую в него вырезанную из index.js функцию - использую для этого буфер обмена.

ItHVmKaQwO.png

oTV8IE5kw3.png

Чтобы эта функция не потерялась в index.html, открываю этот шаблон и дописываю в JS-ассеты только что созданный файл.

gmpX8Nr00T.png

Запускаю отладочный сервер и проверяю в браузере стартовую страницу, обращаю внимание, что часы тикают и показывают время и дату в заданном формате.

ZvX9WXPctq.png

Рефакторинг прошёл успешно, и теперь у меня есть возможность использовать функцию showDateTime, просто присоединив соответствующий файл к соответствующему асету соответствующего шаблона. Приступим непосредственно к вёрстке целевого шаблона для страницы входа в сервис, но прежде обратим внимание на некоторые особенности и ключевые моменты.

Первое. Система авторизации пользователей selfish кроме страницы входа в сервис будет иметь несколько других страниц, при помощи которых будет осуществляться регистрация и обслуживание аккаунта пользователя, а именно:

  • смена пароля;
  • восстановление забытого пароля;
  • регистрация аккаунта;
  • регистрация пользователя;
  • смена адреса электронной почты.

Все страницы системы авторизации будут иметь абсолютно идентичное оформление и структуру функциональных элементов, поэтому имеет смысл объединить их базовым шаблоном системы авторизации. Создаю файл базового шаблона системы авторизации в уже созданном ранее каталоге.

touch selfish/templates/auth/auth_base.html

Второе. Элементы интерфейса на перечисленных страницах системы авторизации будут абсолютно идентичны, и некоторые из них будут повторяться. Чтобы в шаблонах не повторять целые блоки кода, я опишу повторяющиеся элементы при помощи соответствующих macro. Создаю ещё один каталог и в нём новый файл.

mkdir selfish/templates/macros
touch selfish/templates/macros/_auth.html

n9jrePsKhh.png

Открываю в редакторе файл auth_base.html и даю ему такой код.

jZ0x9ecYp8.png

Как видно на снимке экрана выше, созданный шаблон является расширением базового шаблона проекта. Теперь открываю шаблон страницы входа в сервис - selfish/templates/auth/login.html, пишу в него следующее.

{% extends "auth/auth_base.html" %}

{% block title %}Вход в сервис{% endblock %}

{% block form_content %}
  <div class="form-block content-block"></div>
{% endblock form_content %}

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

Bi4gcOXKDr.png

На странице появилось главное меню selfish и подвал, центр страницы предстоит сформировать в процессе вёрстки шаблона login.html, но прежде я разработаю запланированные macro в файле _auth.html, отрываю его в редакторе.

Мне нужно каким-то образом обрабатывать и отображать информационные flash-сообщения. Для этого пишу в файл _auth.html следующий macro.

{% macro get_message(messages) %}
  {% for message in messages %}
    <div class="flashed-message {% if not loop.first %}next-block{% endif %}">
      <div class="alert alert-warning">
        <button class="close" type="button" data-dismiss="alert">
            &times;</button>
          {{ message }}
      </div>
    </div>
  {% endfor %}
{% endmacro %}

Мне необходимо обрабатывать и отображать ошибки валидации формы, для этого свой macro.

{% macro show_error(form, field) %}
  {% if form.errors[field] %}
    {% for error in form.errors[field] %}
      <div class="error">{{ error }}</div>
    {% endfor %}
  {% endif %}
{% endmacro %}

Мне необходимо обрабатывать и отображать поля формы для ввода пользовательских данных, для этого свой macro.

{% macro render_form_group(form, field, fieldname, placeholder) %}
  <div class="form-group {% if form.errors[fieldname] %}has-error{% endif %}">
    <div class="form-label {% if form.errors[fieldname] %}error{% endif %}
                text-right">
      {{ field.label }}
    </div>
    <div class="form-input">
      {{ field(class="form-control", placeholder=placeholder) }}
      {{ show_error(form, fieldname) }}
    </div>
  </div>
{% endmacro %}

Форма для входа в сервис содержит поле с флажком, для его отображения следующий macro.

{% macro render_form_boolean(form, field, checked=False) %}
  <div class="form-group">
    <div class="form-input checkbox">
      <label>
        {{ field(checked=checked) }}{{ field.label }}
      </label>
    </div>
  </div>
{% endmacro %}

XATB2JdLzP.png

Когда все macro созданы, возвращаюсь к файлу login.html, импортирую в него все вспомогательные macro и формирую интерфейс страницы входа в сервис.

VUAfVUgjyr.png

Обновляю страницу в браузере и убеждаюсь, что выглядит всё пока очень непрезентабельно.

L4hGMvfLoR.png

Пришла пора поработать над внешним видом страниц системы авторизации. Напомню, все страницы системы авторизации в перспективе будут иметь абсолютно аналогичный внешний вид, чтобы добиться такого эффекта я буду править базовый шаблон системы авторизации - файл selfish/templates/auth/auth_base.html, и дописывать в этот файл соответствующие таблицы стилей и JS-сценарии. Открываю указанный файл в редакторе и модифицирую блок styles следующим образом.

{% block styles %}
  {{ super() }}
  {% assets filters='cssmin', output='generic/css/auth/auth.css',
            'css/base.css', 'css/content-block.css',
            'css/auth/form-block.css' %}
    <link rel="stylesheet" type="text/css" href="{{ ASSET_URL }}">
  {% endassets %}
{% endblock styles %}

Создаю только что добавленные в этот блок файлы с таблицами стилей.

mkdir selfish/static/css/auth
touch selfish/static/css/content-block.css
touch selfish/static/css/auth/form-block.css

Начинаю с файла content-block.css, открываю его в редакторе и даю ему следующий код.

.content-block {
    border-radius: 3px;
    border: 1px solid #d3ddd3;
    box-shadow: 0 0 6px #f1dbc2;
    }

.block-header {
    border-radius: 3px;
    background: linear-gradient(to bottom, #fffef2, #f3f2e7);
    padding: 10px;
    }

.block-body {
    margin: 8px 12px 12px;
    }

.today-field {
    font-size: 0.85em;
    font-style: italic;
    font-weight: 600;
    }

.form-help {
    font-style: italic;
    text-align: justify;
    margin: 6px 0;
    }

.form-help p {
    margin: 2px 0 0;
    }

.next-block {
    margin-top: 6px;
    }

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

sE9y62cJDH.png

Уже лучше, но желаемый результат пока не достигнут. Открываю в редакторе файл selfish/static/css/auth/form-block.css и пишу в него такой код.

.form-block {
    margin: 20px 0 20px 0;
    }

.form-form {
    margin: 0;
    }

.form-group {
    margin: 6px 0 0;
    }

.form-label {
    width: 25%;
    float: left;
    padding-top: 7px;
    }

.checkbox {
    margin: 0;
    }

.checkbox label label {
    padding-left: 0;
    }

.form-input {
    margin-left: 26%;
    }

Обновляю страницу в браузере.

QCGFj4TSVm.png

Замечательный результат, но немного JavaScript на странице совсем не помешает. Возвращаюсь в редакторе к шаблону auth_base.html и привожу блок scripts этого шаблона к следующему виду.

{% block scripts %}
  {{ super() }}
  {% assets filters='jsmin', output='generic/js/auth.js',
            'js/custom_moment.js', 'js/show-datetime.js', 'js/auth/auth.js' %}
    <script src="{{ ASSET_URL }}"></script>
  {% endassets %}
{% endblock scripts %}

Создаю новый каталог.

mkdir selfish/static/js/auth

Копирую в этот каталог файл selfish/static/js/main/index.js с новым именем.

cp selfish/static/js/main/index.js selfish/static/js/auth/auth.js

На данном этапе разработки код этих файлов абсолютно идентичен, но использовать index.js в базовом шаблоне системы авторизации не следует, потому что в процессе разработки index.js получит новый код, который страницам системы авторизации не нужен, поэтому я и создал новый файл. Обновляю страницу в браузере.

uC7QQiMtEe.png

На странице появились часики, отсчитывающие время. Осталось дописать новые функциональные url-адреса приложения в главное меню selfish, и таким образом дать возможность пользователям сервиса заходить на страницу входа и выходить из сервиса посредством этих ссылок. Открываю в редакторе базовый шаблон приложения - файл selfish/templates/base.html и нахожу в этом файле целевой тег <ul class="nav navbar-nav navbar-right">.

195gEdFltN.png

В этом теге я и допишу новые целевые ссылки, привожу этот тег к следующему виду.

<ul class="nav navbar-nav navbar-right">
  <li class="dropdown">
    {% if current_user.is_authenticated %}
    <a href="#" class="dropdown-toggle" data-toggle="dropdown">
      <img alt="avatar"
           src="{{ current_user.account.get_ava_url(size=22) }}">
      <span
         class="current-user-name">{{ current_user.username }}</span>
      <b class="caret"></b>
    </a>
    {% else %}
    <a href="#" class="dropdown-toggle" data-toggle="dropdown">
      Действия <b class="caret"></b>
    </a>
    {% endif %}
    <ul class="dropdown-menu">
      {% if current_user.is_authenticated %}
      <li><a href="{{ url_for('auth.logout') }}">Выйти</a></li>
      <li><a href="">Профиль</a></li>
      {% else %}
      <li><a href="{{ url_for('auth.login') }}">Войти</a></li>
      <li><a href="">Зарегистрироваться</a></li>
      {% endif %}
    </ul>
  </li>
</ul>

Все правки кода завершены, настало время протестировать полученное решение в ручном режиме в браузере. Тестирование - основополагающий этап разработки. Современная парадигма web-разработки выделяет два типа тестирования:

  • ручное тестирование в браузере;
  • автоматизированное тестирование с помощью соответствующих скриптов.

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

Запускаю отладочный сервер и открываю в браузере стартовую страницу приложения.

ndjkLoan1E.png

В главном меню нажимаю пункт "Действия" и нахожу ссылку "Войти".

eQgzJu457r.png

Перехожу по этой ссылке и оказываюсь на странице ввода логина и пароля.

dfb8E8S8A5.png

В одном из предыдущих выпусков блога я создал в приложении пользователя newbie с адресом электронной почты newbie@auriz.ru, пробую войти с псевдонимом этого пользователя и правильным паролем.

ZhT7mLHNXH.png

YESMyAo2Kp.png

Оказываюсь на стартовой странице selfish, но при этом в главном меню приложения вижу аватар и псевдоним авторизованного пользователя. Нажимаю левой кнопкой мыши на этом пункте главного меню и обнаруживаю ссылку "Выйти".

OY4Yti9u84.png

Следую по этой ссылке и опять оказываюсь на стартовой странице приложения анонимным пользователем.

BZwWdczKbz.png

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

BjjZkSdQoH.png

Убеждаюсь, что система приняла и этот вариант. Находясь в сервисе авторизованным пользователем, ввожу в адресную строку браузера адрес страницы входа в сервис.

xq5556BUGw.png

Нажимаю enter и убеждаюсь, что selfish перенаправил меня на стартовую страницу.

YxUNDABrhi.png

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

klF9WigEcS.png

И получаю вежливый отказ в авторизации.

sV3ZGkQQ8u.png

Перехожу на стартовую страницу приложения и ввожу в адресную строку браузера url выхода из сервиса.

VIg0nf1kcp.png

Нажимаю enter и оказываюсь на странице входа в сервис, при этом обращаю внимание на url-адрес в адресной строке браузера.

SHT8qBIgTQ.png

Можно придумать какие-то ещё дополнительные тестовые сценарии, не буду демонстрировать их все снимками экрана, на текущий момент логика входа в сервис на стороне сервера работает без замечаний и сбоев, а внешний вид и функциональные элементы страниц браузера не имеют явных изъянов и недостатков. Можно сделать вывод, что разработанный на этом этапе проектирования функционал работает в полном соответствии с замыслом, а значит все поставленные цели достигнуты и можно переходить к следующим этапам разработки. Текущий код selfish можно увидеть в моём профиле на gitlab.com. А я вынужден сообщить, что продолжение следует, и в следующем выпуске блога мы поговорим о разрешениях пользователя.

Комментарии: