One place for hosting & domains

      приложение

      Добавление аутентификации в ваше приложение с помощью Flask-Login


      Введение

      Предоставление пользователям возможности ввода учетных данных для входа — одна из самых распространенных возможностей, добавляемых в веб-приложения. В этой статье мы расскажем, как добавить аутентификацию в ваше приложение Flask с помощью пакета Flask-Login.

      Анимированный файл gif с изображением приложения Flask и поля входа

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

      В этой статье мы затронем следующие вопросы:

      • Использование библиотеки Flask-Login для управления сеансами
      • Использование встроенной утилиты Flask для хэширования паролей
      • Добавление в приложение защищенных страниц для пользователей, не выполнивших вход
      • Использование Flask-SQLAlchemy для создания пользовательской модели
      • Создание форм регистрации и входа для создания учетных записей пользователей и входа
      • Вывод пользователям сообщений об ошибках, если что-то идет не так
      • Использование информации учетной записи пользователя для отображения на странице профиля

      Исходный код этого проекта доступен на GitHub.

      Предварительные требования

      Для этого обучающего модуля вам потребуется следующее:

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

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

      .
      └── flask_auth_app
          └── project
              ├── __init__.py       # setup our app
              ├── auth.py           # the auth routes for our app
              ├── db.sqlite         # our database
              ├── main.py           # the non-auth routes for our app
              ├── models.py         # our user model
              └── templates
                  ├── base.html     # contains common layout and links
                  ├── index.html    # show the home page
                  ├── login.html    # show the login form
                  ├── profile.html  # show the profile page
                  └── signup.html   # show the signup form
      

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

      Шаг 1 — Установка пакетов

      Для нашего проекта нам потребуется три основных проекта:

      • Flask
      • Flask-Login: для обработки пользовательских сеансов после аутентификации
      • Flask-SQLAlchemy: для представления пользовательской модели и интерфейса с нашей базой данных

      Мы будем использовать SQLite, чтобы не устанавливать дополнительные зависимости для базы данных.

      Для начала мы создадим каталог проекта:

      Затем нам нужно будет перейти в каталог проекта:

      Если у нас нет среды Python, нам нужно будет ее создать. В зависимости от способа установки Python на вашем компьютере команды будут выглядеть примерно так:

      • python3 -m venv auth
      • source auth/bin/activate

      Примечание. Вы можете использовать учебный модуль по вашей локальной среде для получения информации по настройке venv.

      Выполните в своей виртуальной среде следующие команды для установки необходимых пакетов:

      • pip install flask flask-sqlalchemy flask-login

      Мы установили пакеты и теперь можем создать основной файл приложения.

      Шаг 2 — Создание главного файла приложения

      Для начала мы создадим каталог проекта:

      В первую очередь мы начнем работать над файлом __init__.py для нашего проекта:

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

      project/__init__.py

      from flask import Flask
      from flask_sqlalchemy import SQLAlchemy
      
      # init SQLAlchemy so we can use it later in our models
      db = SQLAlchemy()
      
      def create_app():
          app = Flask(__name__)
      
          app.config['SECRET_KEY'] = 'secret-key-goes-here'
          app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite'
      
          db.init_app(app)
      
          # blueprint for auth routes in our app
          from .auth import auth as auth_blueprint
          app.register_blueprint(auth_blueprint)
      
          # blueprint for non-auth parts of app
          from .main import main as main_blueprint
          app.register_blueprint(main_blueprint)
      
          return app
      

      Теперь у нас готов основной файл приложения, и мы можем начать добавление маршрутов.

      Шаг 3 — Добавление маршрутов

      Для наших маршрутов мы будем использовать два проекта. В основном проекте у нас будет главная страница (/) и страница профиля (/profile), открываемая после входа. Если пользователь попытается получить доступ к странице профиля без входа в систему, он будет направлен на маршрут входа.

      В нашем проекте auth у нас будут маршруты для получения страницы входа (/login) и страницы регистрации (/sign-up). Также у нас имеются маршруты для обработки запросов POST от обоих этих маршрутов. Наконец, у нас имеется маршрут выхода (/logout) для выхода активного пользователя из системы.

      Пока что мы определим login, signup и logout простыми возвратами. Мы вернемся к ним немного позднее и обновим их, добавив желаемые функции.

      Вначале создайте файл main.py для main_blueprint:

      project/main.py

      from flask import Blueprint
      from . import db
      
      main = Blueprint('main', __name__)
      
      @main.route('/')
      def index():
          return 'Index'
      
      @main.route('/profile')
      def profile():
          return 'Profile'
      

      Затем создайте файл auth.py для auth_blueprint:

      project/auth.py

      from flask import Blueprint
      from . import db
      
      auth = Blueprint('auth', __name__)
      
      @auth.route('/login')
      def login():
          return 'Login'
      
      @auth.route('/signup')
      def signup():
          return 'Signup'
      
      @auth.route('/logout')
      def logout():
          return 'Logout'
      

      В терминале вы можете задать значения FLASK_APP и FLASK_DEBUG:

      • export FLASK_APP=project
      • export FLASK_DEBUG=1

      Переменная среды FLASK_APP сообщает Flask, как загружать приложение. Она должна указывать на место создания create_app. Для наших целей мы будем использовать указатели на каталог project.

      Переменная среды FLASK_DEBUG активируется посредством присвоения ей значения 1. Это активирует отладчик, который будет отображать в браузере ошибки приложения.

      Убедитесь, что вы находитесь в каталоге flask_auth_app и запустите проект:

      Теперь у вас должна появиться возможность открыть в браузере пять возможных URL-адресов и увидеть возвращаемый текст, определенный в файлах auth.py и main.py.

      Например, если открыть адрес localhost:5000/profile, появится: Profile:

      Снимок экрана проекта с открытым адресом localhost:5000 в браузере

      Мы проверили поведение наших маршрутов и можем перейти к созданию шаблонов.

      Шаг 4 — Создание шаблонов

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

      • index.html
      • profile.html
      • login.html
      • signup.html

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

      Для начала создайте каталог templates в каталоге project:

      • mkdir -p project/templates

      Затем создайте base.html:

      • nano project/templates/base.html

      Добавьте следующий код в файл base.html:

      project/templates/base.html

      <!DOCTYPE html>
      <html>
      
      <head>
          <meta charset="utf-8">
          <meta http-equiv="X-UA-Compatible" content="IE=edge">
          <meta name="viewport" content="width=device-width, initial-scale=1">
          <title>Flask Auth Example</title>
          <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.min.css" />
      </head>
      
      <body>
          <section class="hero is-primary is-fullheight">
      
              <div class="hero-head">
                  <nav class="navbar">
                      <div class="container">
      
                          <div id="navbarMenuHeroA" class="navbar-menu">
                              <div class="navbar-end">
                                  <a href="https://www.digitalocean.com/community/tutorials/{{ url_for("main.index') }}" class="navbar-item">
                                      Home
                                  </a>
                                  <a href="https://www.digitalocean.com/community/tutorials/{{ url_for("main.profile') }}" class="navbar-item">
                                      Profile
                                  </a>
                                  <a href="https://www.digitalocean.com/community/tutorials/{{ url_for("auth.login') }}" class="navbar-item">
                                      Login
                                  </a>
                                  <a href="https://www.digitalocean.com/community/tutorials/{{ url_for("auth.signup') }}" class="navbar-item">
                                      Sign Up
                                  </a>
                                  <a href="https://www.digitalocean.com/community/tutorials/{{ url_for("auth.logout') }}" class="navbar-item">
                                      Logout
                                  </a>
                              </div>
                          </div>
                      </div>
                  </nav>
              </div>
      
              <div class="hero-body">
                  <div class="container has-text-centered">
                     {% block content %}
                     {% endblock %}
                  </div>
              </div>
          </section>
      </body>
      
      </html>
      

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

      Примечание. За кулисами мы используем Bulma для работы со стилями и макетом. Чтобы узнать больше о Bulma, ознакомьтесь с официальной документацией Bulma.

      Затем создайте файл templates/index.html:

      • nano project/templates/index.html

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

      project/templates/index.html

      {% extends "base.html" %}
      
      {% block content %}
      <h1 class="title">
        Flask Login Example
      </h1>
      <h2 class="subtitle">
        Easy authentication and authorization in Flask.
      </h2>
      {% endblock %}
      

      Этот код создаст базовую страницу указателя с заголовком и подзаголовком.

      Затем создайте страницу templates/login.html:

      • nano project/templates/login.html

      Этот код генерирует страницу входа с полями Email и Password. Также имеется поле для отметки запоминания сеанса входа.

      project/templates/login.html

      {% extends "base.html" %}
      
      {% block content %}
      <div class="column is-4 is-offset-4">
          <h3 class="title">Login</h3>
          <div class="box">
              <form method="POST" action="/login">
                  <div class="field">
                      <div class="control">
                          <input class="input is-large" type="email" name="email" placeholder="Your Email" autofocus="">
                      </div>
                  </div>
      
                  <div class="field">
                      <div class="control">
                          <input class="input is-large" type="password" name="password" placeholder="Your Password">
                      </div>
                  </div>
                  <div class="field">
                      <label class="checkbox">
                          <input type="checkbox">
                          Remember me
                      </label>
                  </div>
                  <button class="button is-block is-info is-large is-fullwidth">Login</button>
              </form>
          </div>
      </div>
      {% endblock %}
      

      Создайте шаблон templates/signup.html:

      • nano project/templates/signup.html

      Добавьте в файл следующий код, чтобы создать страницу регистрации с полями адреса электронной почты, имени и пароля:

      project/templates/signup.html

      {% extends "base.html" %}
      
      {% block content %}
      <div class="column is-4 is-offset-4">
          <h3 class="title">Sign Up</h3>
          <div class="box">
              <form method="POST" action="/signup">
                  <div class="field">
                      <div class="control">
                          <input class="input is-large" type="email" name="email" placeholder="Email" autofocus="">
                      </div>
                  </div>
      
                  <div class="field">
                      <div class="control">
                          <input class="input is-large" type="text" name="name" placeholder="Name" autofocus="">
                      </div>
                  </div>
      
                  <div class="field">
                      <div class="control">
                          <input class="input is-large" type="password" name="password" placeholder="Password">
                      </div>
                  </div>
      
                  <button class="button is-block is-info is-large is-fullwidth">Sign Up</button>
              </form>
          </div>
      </div>
      {% endblock %}
      

      Затем создайте шаблон templates/profile.html:

      • nano project/templates/profile.html

      Добавьте этот код, чтобы создать простую страницу с закодированным заголовком, welcome Anthony:

      project/templates/profile.html

      {% extends "base.html" %}
      
      {% block content %}
      <h1 class="title">
        Welcome, Anthony!
      </h1>
      {% endblock %}
      

      Позднее мы добавим код для динамического приветствия пользователя.

      После добавления шаблонов мы можем обновить выражения возврата в каждом из маршрутов, чтобы вместо текста возвращались шаблоны.

      Обновите файл main.py, изменив строку импорта и маршруты index и profile:

      project/main.py

      from flask import Blueprint, render_template
      ...
      @main.route('/')
      def index():
          return render_template('index.html')
      
      @main.route('/profile')
      def profile():
          return render_template('profile.html')
      

      Теперь мы обновим auth.py, изменив строку импорта и маршруты login и signup:

      project/auth.py

      from flask import Blueprint, render_template
      ...
      @auth.route('/login')
      def login():
          return render_template('login.html')
      
      @auth.route('/signup')
      def signup():
          return render_template('signup.html')
      

      После внесения этих изменений страница регистрации будет следующим образом при переходе в /sign-up:

      Страница регистрации в /signup

      Теперь вы должны видеть страницы /, /login и /profile.

      Пока что мы оставим /logout отдельно, потому что эта страница не отображает шаблон.

      Шаг 5 — Создание пользовательской модели

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

      Модели, созданные в Flask-SQLAlchemy, представляются классами, которые преобразуются в таблицу в базе данных. Атрибуты этих классов превратятся в столбцы этих таблиц.

      Давайте вместе создадим эту пользовательскую модель:

      Этот код создает пользовательскую модель со столбцами id, email, password и name:

      project/models.py

      from . import db
      
      class User(db.Model):
          id = db.Column(db.Integer, primary_key=True) # primary keys are required by SQLAlchemy
          email = db.Column(db.String(100), unique=True)
          password = db.Column(db.String(100))
          name = db.Column(db.String(1000))
      

      Мы создали пользовательскую модель и теперь можем перейти к настройке базы данных.

      Шаг 6 — Настройка базы данных

      Как указано в разделе «Предварительные требования», мы будем использовать базу данных SQLite. Мы можем создать базу данных SQLite самостоятельно, но сейчас используем для этого Flask-SQLAlchemy. Мы уже указали путь к базе данных в файле __init__.py, так что нам нужно просто указать Flask-SQLAlchemy создать базу данных на Python REPL.

      Если вы остановите приложение и откроете Python REPL, мы сможем создать базу данных, используя метод create_all для объекта db. Убедитесь, что вы все еще находитесь в виртуальной среде и в каталоге flask_auth_app.

      • from project import db, create_app
      • db.create_all(app=create_app()) # pass the create_app result so Flask-SQLAlchemy gets the configuration.

      Примечание. Если вы незнакомы с использованием интерпретатора Python, вы можете проконсультироваться с официальной документацией.

      Теперь вы видите файл db.sqlite в каталоге проекта. В этой базе данных будет наша пользовательская таблица.

      Шаг 7 — Настройка функции авторизации

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

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

      Создайте функцию и добавьте в ее конец переадресацию. В результате после успешной регистрации пользователь будет переадресован на страницу входа.

      Обновите файл auth.py, изменив строку import и реализовав signup_post:

      project/auth.py

      from flask import Blueprint, render_template, redirect, url_for
      ...
      @auth.route('/signup', methods=['POST'])
      def signup_post():
          # code to validate and add user to database goes here
          return redirect(url_for('auth.login'))
      

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

      Для начала нам нужно будет использовать объект запроса для получения данных формы.

      Продолжим обновлять файл auth.py, добавляя элементы импорта и реализуя signup_post:

      auth.py

      from flask import Blueprint, render_template, redirect, url_for, request
      from werkzeug.security import generate_password_hash, check_password_hash
      from .models import User
      from . import db
      ...
      @auth.route('/signup', methods=['POST'])
      def signup_post():
          email = request.form.get('email')
          name = request.form.get('name')
          password = request.form.get('password')
      
          user = User.query.filter_by(email=email).first() # if this returns a user, then the email already exists in database
      
          if user: # if a user is found, we want to redirect back to signup page so user can try again
              return redirect(url_for('auth.signup'))
      
          # create a new user with the form data. Hash the password so the plaintext version isn't saved.
          new_user = User(email=email, name=name, password=generate_password_hash(password, method='sha256'))
      
          # add the new user to the database
          db.session.add(new_user)
          db.session.commit()
      
          return redirect(url_for('auth.login'))
      

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

      Шаг 8 — Тестирование метода регистрации

      У нас готов метод регистрации, и теперь мы можем создать нового пользователя. Используйте форму для создания пользователя.

      Существует два способа проверить регистрацию: использовать инструмент просмотра БД для поиска строки, добавленную в таблицу, или попробовать зарегистрироваться с тем же адресом электронной почты, и если вы получите сообщение об ошибке, это будет означать, что первый адрес электронной почты был сохранен правильно. Используем этот подход.

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

      Вначале мы добавим элемент flash, выводимый до возврата на страницу регистрации.

      project/auth.py

      from flask import Blueprint, render_template, redirect, url_for, request, flash
      ...
      @auth.route('/signup', methods=['POST'])
      def signup_post():
          ...
          if user: # if a user is found, we want to redirect back to signup page so user can try again
              flash('Email address already exists')
              return redirect(url_for('auth.signup'))
      

      Чтобы добавить в шаблон мигающее сообщение, нужно добавить над формой этот код. Так сообщение будет выводиться непосредственно над формой.

      project/templates/signup.html

      ...
      {% with messages = get_flashed_messages() %}
      {% if messages %}
          <div class="notification is-danger">
              {{ messages[0] }}. Go to <a href="https://www.digitalocean.com/community/tutorials/{{ url_for("auth.login') }}">login page</a>.
          </div>
      {% endif %}
      {% endwith %}
      <form method="POST" action="/signup">
      

      Поле регистрации с сообщением «Email address already exists. Go to login page» в темно-розовом поле

      Шаг 9 — Добавление метода входа

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

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

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

      project/auth.py

      ...
      @auth.route('/login', methods=['POST'])
      def login_post():
          # login code goes here
          return redirect(url_for('main.profile'))
      

      Теперь нам нужно убедиться, что учетные данные пользователя введены верно:

      project/auth.py

      ...
      @auth.route('/login', methods=['POST'])
      def login_post():
          email = request.form.get('email')
          password = request.form.get('password')
          remember = True if request.form.get('remember') else False
      
          user = User.query.filter_by(email=email).first()
      
          # check if the user actually exists
          # take the user-supplied password, hash it, and compare it to the hashed password in the database
          if not user or not check_password_hash(user.password, password):
              flash('Please check your login details and try again.')
              return redirect(url_for('auth.login')) # if the user doesn't exist or password is wrong, reload the page
      
          # if the above check passes, then we know the user has the right credentials
          return redirect(url_for('main.profile'))
      

      Добавим в шаблон блок, чтобы пользователь мог видеть мигающее сообщение. Как и в случае с формой регистрации, добавим потенциальное сообщение об ошибке непосредственно над формой:

      project/templates/login.html

      ...
      {% with messages = get_flashed_messages() %}
      {% if messages %}
          <div class="notification is-danger">
              {{ messages[0] }}
          </div>
      {% endif %}
      {% endwith %}
      <form method="POST" action="/login">
      

      Теперь мы можем указать, что пользователь успешно выполнил вход, но пользователю пока некуда входить. На этом этапе мы используем Flask-Login для управления сеансами пользователя.

      Прежде чем начать, нам потребуется несколько вещей для работы Flask-Login. Для начала добавьте UserMixin в пользовательскую модель. UserMixin добавит в модель атрибуты Flask-Login, чтобы Flask-Login мог с ней работать.

      models.py

      from flask_login import UserMixin
      from . import db
      
      class User(UserMixin, db.Model):
          id = db.Column(db.Integer, primary_key=True) # primary keys are required by SQLAlchemy
          email = db.Column(db.String(100), unique=True)
          password = db.Column(db.String(100))
          name = db.Column(db.String(1000))
      

      Затем нам нужно указать загрузчик пользователя. Загрузчик пользователя сообщает Flask-Login, как найти определенного пользователя по идентификатору, сохраненному в файле cookie сеанса. Мы можем добавить его в функцию create_app вместе с кодом init для Flask-Login:

      project/__init__.py

      ...
      from flask_login import LoginManager
      ...
      def create_app():
          ...
          db.init_app(app)
      
          login_manager = LoginManager()
          login_manager.login_view = 'auth.login'
          login_manager.init_app(app)
      
          from .models import User
      
          @login_manager.user_loader
          def load_user(user_id):
              # since the user_id is just the primary key of our user table, use it in the query for the user
              return User.query.get(int(user_id))
      

      В заключение мы можем добавить функцию login_user перед переадресацией на страницу профиля для создания сеанса:

      project/auth.py

      from flask_login import login_user
      from .models import User
      ...
      @auth.route('/login', methods=['POST'])
      def login_post():
          ...
          # if the above check passes, then we know the user has the right credentials
          login_user(user, remember=remember)
          return redirect(url_for('main.profile'))
      

      С помощью Flask-Login мы можем использовать маршрут /login. Когда все размещено правильно, вы увидите страницу профиля.

      Страница профиля с текстом «Welcome, Anthony!»

      Шаг 10 — Защита страниц

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

      Чтобы защитить страницу при использовании Flask-Login, мы добавим декоратор @login_requried между маршрутом и функцией. Это не даст пользователю, не выполнившему вход в систему, увидеть этот маршрут. Если пользователь не выполнил вход, он будет переадресован на страницу входа согласно конфигурации Flask-Login.

      Используя маршруты с декоратором @login_required, мы можем использовать объект current_user внутри функций. Этот объект current_user представляет пользователя из базы данных, и мы можем получить доступ ко всем атрибутам этого пользователя, используя точечную нотацию. Например, current_user.email, current_user.password, current_user.name и current_user.id будут возвращать реальные значения, хранящиеся в базе данных для пользователя, который выполнил вход в систему.

      Давайте используем имя текущего пользователя и отправим его в шаблон. Затем мы используем это имя и выведем его значение.

      project/main.py

      from flask_login import login_required, current_user
      ...
      @main.route('/profile')
      @login_required
      def profile():
          return render_template('profile.html', name=current_user.name)
      

      Затем в файле profile.html мы обновим страницу для отображения значения name:

      project/templates/profile.html

      ...
      <h1 class="title">
        Welcome, {{ name }}!
      </h1>
      

      Когда мы перейдем на страницу профиля, мы увидим, что там отображается имя пользователя.

      Страница приветствия пользователя с именем пользователя, выполнившего вход в систему

      В заключение мы можем обновить представление выхода из системы. Мы можем вызвать функцию logout_user в маршруте выхода. Мы используем декоратор @login_required, потому что не имеет смысла выполнять выход для пользователя, который не выполнил вход.

      project/auth.py

      from flask_login import login_user, logout_user, login_required
      ...
      @auth.route('/logout')
      @login_required
      def logout():
          logout_user()
          return redirect(url_for('main.index'))
      

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

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

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

      templates/base.html

      ...
      <div class="navbar-end">
          <a href="https://www.digitalocean.com/community/tutorials/{{ url_for("main.index') }}" class="navbar-item">
              Home
          </a>
          {% if current_user.is_authenticated %}
          <a href="https://www.digitalocean.com/community/tutorials/{{ url_for("main.profile') }}" class="navbar-item">
              Profile
          </a>
          {% endif %}
          {% if not current_user.is_authenticated %}
          <a href="https://www.digitalocean.com/community/tutorials/{{ url_for("auth.login') }}" class="navbar-item">
              Login
          </a>
          <a href="https://www.digitalocean.com/community/tutorials/{{ url_for("auth.signup') }}" class="navbar-item">
              Sign Up
          </a>
          {% endif %}
          {% if current_user.is_authenticated %}
          <a href="https://www.digitalocean.com/community/tutorials/{{ url_for("auth.logout') }}" class="navbar-item">
              Logout
          </a>
          {% endif %}
      </div>
      

      Главная страница с элементами навигации Home (Главная), Login (Вход) и Sign Up (Регистрация) в верхней части экрана

      Мы успешно создали приложение с аутентификацией.

      Заключение

      Мы использовали Flask-Login и Flask-SQLAlchemy для создания системы входа в наше приложение. Мы рассказали о том, как организовать аутентификацию пользователей посредством создания пользовательской модели и сохранения данных пользователя. Затем нам нужно было проверить правильность пароля пользователя, выполнив хэширование пароля из формы, и сравнив его с сохраненным в базе данных. В заключение мы добавили в приложение авторизацию, используя декоратор @login_required на странице профиля, чтобы пользователи могли видеть ее только после входа.

      Того, что мы создали в этом учебном модуле, будет достаточно для небольших приложений, но если вы хотите с самого начала использовать больше функций, подумайте об использовании библиотек Flask-User или Flask-Security, которые построены на базе библиотеки Flask-Login.



      Source link

      Добавление Sidekiq и Redis в приложение Ruby on Rails


      Введение

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

      Sidekiq — одна из наиболее широко используемых инфраструктур фоновых задач, которые можно реализовать в приложении Rails. Она основана на системе хранения пар ключ-значение в оперативной памяти Redis, отличающейся гибкостью и высокой производительностью. Sidekiq использует Redis как хранилище для управления задачами, чтобы обрабатывать тысячи задач в секунду.

      В этом обучающем руководстве мы добавим Redis и Sidekiq в существующее приложение Rails. Мы создадим набор рабочих классов Sidekiq и методов для обработки:

      • Пакетная выгрузка информации о находящихся под угрозой вымирания акулах в базу данных приложения из файла CSV в репозитории проекта.
      • Удаление этих данных.

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

      Предварительные требования

      Для данного обучающего руководства вам потребуется следующее:

      • Локальный компьютер или сервер разработки под управлением Ubuntu 18.04. На используемом для разработки компьютере должен быть пользователь без привилегий root с административными привилегиями, а также брандмауэр, настроенный с помощью ufw. Указания по настройке можно найти в обучающем руководстве Начальная настройка сервера Ubuntu 18.04.
      • Node.js и npm, установленные на локальном компьютере или сервере разработки. В этом обучающем руководстве используются версии Node.js 10.17.0 и npm 6.11.3. Указания по установке Node.js и npm в Ubuntu 18.04 можно найти в разделе Установка с использованием PPA обучающего руководства Установка Node.js в Ubuntu 18.04.
      • Диспетчер пакетов Yarn, установленный на локальном компьютере или сервере разработки. Вы можете следовать указаниям по установке, приведенным в официальной документации.
      • Ruby, rbenv и Rails, установленные на локальном компьютере или сервере разработки в соответствии с указаниями шагов 1-4 обучающего руководства Установка Ruby on Rails с помощью rbenv в Ubuntu 18.04. В этом обучающем руководстве используются версии Ruby 2.5.1, rbenv 1.1.2 и Rails 5.2.3.
      • СУБД SQLite, установленная в соответствии с указаниями шага 1 обучающего руководства Создание приложения Ruby on Rails. В этом обучающем руководстве используется версия СУБД SQLite 3 3.22.0.
      • Система Redis, установленная в соответствии с указаниями шагов 1-3 обучающего руководства Установка и защита Redis в Ubuntu 18.04. В этом обучающем руководстве используется версия Redis 4.0.9.

      Шаг 1 — Клонирование проекта и установка зависимостей

      Нашим первым шагом будет клонирование репозитория rails-bootstrap из учетной записи DigitalOcean Community на GitHub. Этот репозиторий содержит код установки, описанный в обучающем руководстве Добавление Bootstrap в приложение Ruby on Rails, где объясняется процедура добавления Bootstrap в существующий проект Rails 5.

      Клонируйте репозиторий в директорию с именем rails-sidekiq:

      • git clone https://github.com/do-community/rails-bootstrap.git rails-sidekiq

      Перейдите в директорию rails-sidekiq:

      Для работы с кодом необходимо предварительно установить зависимости проекта, перечисленные в файле Gemfile. Также вам потребуется добавить в проект sidekiq gem для работы с Sidekiq и Redis.

      Откройте файл проекта Gemfile для редактирования, используя nano или другой предпочитаемый редактор:

      Добавьте зависимость в любое место в списке основных зависимостей проекта (над зависимостями разработки):

      ~/rails-sidekiq/Gemfile

      . . .
      # Reduces boot times through caching; required in config/boot.rb
      gem 'bootsnap', '>= 1.1.0', require: false
      gem 'sidekiq', '~>6.0.0'
      
      group :development, :test do
      . . .
      

      Сохраните и закройте файл после добавления зависимости.

      Используйте для установки зависимостей следующую команду:

      В результатах вы увидите, что зависимость redis также установлена как требование для sidekiq.

      Далее мы установим зависимости Yarn. Поскольку данный проект Rails 5 был модифицирован для обслуживания ресурсов с пакетом webpack, его зависимостями JavaScript теперь управляет Yarn. Это означает, что необходимо установить и проверить зависимости, указанные в файле проекта package.json.

      Используйте команду yarn install для установки этих зависимостей:

      Затем проведите миграцию базы данных:

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

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

      • bundle exec rails s --binding=your_server_ip

      Перейдите на адрес localhost:3000 или http://your_server_ip:3000. Вы увидите следующую начальную страницу:

      Начальная страница приложения

      Чтобы создать новую акулу, нажмите кнопку Get Shark Info, после чего откроется путь sharks/index:

      Путь указателя акул

      Чтобы убедиться в работе приложения, добавим в него примеры данных. Нажмите New Shark. Вам будет предложено ввести имя пользователя (sammy) и пароль (shark) в связи с параметрами аутентификации проекта.

      На странице New Shark введите Great White в поле Name и Scary в поле Facts:

      Создание акулы

      Нажмите кнопку Create Shark для создания акулы. Когда акула будет создана, вы можете закрыть сервер, нажав CTRL+C.

      Вы установили необходимые зависимости для вашего проекта и протестировали его функционал. Теперь вы можете внести некоторые изменения в приложение Rails для работы с вашими ресурсами по находящимся под угрозой видам акул.

      Шаг 2 — Генерирование контроллера для ресурсов по находящимся под угрозой видам акул

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

      Прежде всего, мы создадим модель Endangered для наших находящихся под угрозой акул. Мы добавим в таблицу нашей базы данных поля строк для названий акул и категорий Международного союза охраны природы (МСОП), определяющих опасность для каждого из видов акул.

      Структура нашей модели будет соответствовать столбцам в файле CSV, который мы будем использовать для пакетной выгрузки данных. Этот файл находится в директории db, и вы можете проверить его содержимое с помощью следующей команды:

      Файл содержит список из 73 видов акул, находящихся под угрозой, и их статусов в МСОП: vu — опасность, en — угроза, cr — критическая угроза.

      Наша модель Endangered коррелирует с этими данными, позволяя создавать новые экземпляры Endangered из этого файла CSV. Создайте модель с помощью следующей команды:

      • rails generate model Endangered name:string iucn:string

      Сгенерируйте контроллер Endangered с помощью действия index:

      • rails generate controller endangered index

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

      Откройте этот файл:

      • nano app/controllers/endangered_controller.rb

      Rails предоставляет нам каркас, который мы можем начать заполнять.

      Прежде всего нам нужно определить, какие маршруты нам потребуются для работы с нашими данными. Благодаря команде generate controller у нас имеется метод index, с которого мы можем начать. Он коррелирует с представлением index, где мы предоставим пользователям возможность выгрузки акул, находящихся под угрозой.

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

      Для начала создадим частный метод set_endangered, который будет получать каждый экземпляр класса Endangered из базы данных. Добавьте в файл следующий код:

      ~/rails-sidekiq/app/controllers/endangered_controller.rb

      class EndangeredController < ApplicationController
        before_action :set_endangered, only: [:index, :data]
      
        def index
        end
      
        private
      
          def set_endangered
            @endangered = Endangered.all
          end
      
      end
      

      Обратите внимание, что фильтр before_action обеспечивает установку значения @endangered только для маршрутов index и data, где мы обрабатываем данные по находящимся под угрозой видам акул.

      Добавьте следующий код в метод index, чтобы определить правильный путь для пользователей, посещающих эту часть приложения:

      ~/rails-sidekiq/app/controllers/endangered_controller.rb

      class EndangeredController < ApplicationController
        before_action :set_endangered, only: [:index, :data]
      
        def index          
          if @endangered.length > 0
            redirect_to endangered_data_path
          else
            render 'index'
          end
        end
      . . .
      

      Если существует более 0 экземпляров нашего класса Endangered, мы перенаправляем пользователей на маршрут data, где они могут просматривать созданную ими информацию об акулах. В ином случае они перейдут к представлению index.

      Под методом index добавьте метод data, который будет коррелровать с представлением data:

      ~/rails-sidekiq/app/controllers/endangered_controller.rb

      . . .
        def index          
          if @endangered.length > 0
            redirect_to endangered_data_path
          else
            render 'index'
          end
        end
      
        def data
        end
      . . .
      

      Затем мы добавим метод для обработки самой процедуры выгрузки данных. Мы назовем этот метод upload. Он будет вызывать класс рабочего Sidekiq и метод для выполнения выгрузки данных из файла CSV. На следующем шаге мы создадим определение для этого рабочего класса AddEndangeredWorker.

      Пока что добавьте в файл следующий код для вызова рабочего Sidekiq, который будет выполнять выгрузку:

      ~/rails-sidekiq/app/controllers/endangered_controller.rb

      . . .
        def data
        end
      
        def upload
          csv_file = File.join Rails.root, 'db', 'sharks.csv'   
          AddEndangeredWorker.perform_async(csv_file)
          redirect_to endangered_data_path, notice: 'Endangered sharks have been uploaded!'
        end
      . . .
      

      Вызывая метод perform_async для класса AddEndangeredWorker с файлом CSV в качестве аргумента, этот код обеспечивает передачу в Redis данных об акулах и задания выгрузки. Заданные нами рабочие Sidekiq будут отслеживать очередь заданий и реагировать при появлении новых заданий.

      После вызова perform_async наш метод upload выполняет перенаправление на путь data, где пользователи смогут просматривать выгруженных акул.

      Далее мы добавим метод destroy для уничтожения данных. Добавьте следующий код после метода upload:

      ~/rails-sidekiq/app/controllers/endangered_controller.rb

      . . .
        def upload
          csv_file = File.join Rails.root, 'db', 'sharks.csv'   
          AddEndangeredWorker.perform_async(csv_file)
          redirect_to endangered_data_path, notice: 'Endangered sharks have been uploaded!'
        end
      
        def destroy
          RemoveEndangeredWorker.perform_async
          redirect_to root_path
        end
      . . .
      

      Как и метод upload, наш метод destroy включает вызов perform_async для класса RemoveEndangeredWorker, еще одного создаваемого нами рабочего Sidekiq. После вызова этот метод перенаправляет пользователей в корневую директорию приложения.

      Итоговый файл будет выглядеть примерно так:

      ~/rails-sidekiq/app/controllers/endangered_controller.rb

      class EndangeredController < ApplicationController
        before_action :set_endangered, only: [:index, :data]
      
        def index          
          if @endangered.length > 0
            redirect_to endangered_data_path
          else
            render 'index'
          end
        end
      
        def data
        end
      
        def upload
          csv_file = File.join Rails.root, 'db', 'sharks.csv'   
          AddEndangeredWorker.perform_async(csv_file)
          redirect_to endangered_data_path, notice: 'Endangered sharks have been uploaded!'
        end
      
        def destroy
          RemoveEndangeredWorker.perform_async
          redirect_to root_path
        end
      
        private
      
          def set_endangered
            @endangered = Endangered.all
          end
      
      end
      

      Сохраните и закройте файл после завершения редактирования.

      В заключение настройки маршрутов приложения мы изменим код в файле config/routes.rb, где хранятся декларации наших маршрутов.

      Откройте этот файл:

      Сейчас файл выглядит следующим образом:

      ~/rails-sidekiq/config/routes.rb

      Rails.application.routes.draw do
        get 'endangered/index'
        get 'home/index'
        resources :sharks do
                resources :posts
        end
        root 'home#index'
        # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
      end
      

      Нам нужно будет обновить файл, чтобы добавить в него определенные в нашем контроллере маршруты: data, upload и destroy. Маршрут data будет соответствовать запросу GET на получение данных об акулах, а маршруты upload и destroy будут соответствовать запросам POST на выгрузку и уничтожение этих данных.

      Добавьте в файл следующий код для определения этих маршрутов:

      ~/rails-sidekiq/config/routes.rb

      Rails.application.routes.draw do
        get 'endangered/index'
        get 'endangered/data', to: 'endangered#data'
        post 'endangered/upload', to: 'endangered#upload'
        post 'endangered/destroy', to: 'endangered#destroy'
        get 'home/index'
        resources :sharks do
                resources :posts
        end
        root 'home#index'
        # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
      end
      

      Сохраните и закройте файл после завершения редактирования.

      Теперь у нас есть модель Endangered и контроллер, и мы можем перейти к определению рабочих классов Sidekiq.

      Шаг 3 — Определение рабочих Sidekiq

      Мы вызываем методы perform_async для рабочих Sidekiq в нашем контроллере, однако нам все еще нужно создать самих рабочих.

      Для начала создайте для рабочих директорию workers:

      Откройте файл для рабочего AddEndangeredWorker:

      • nano app/workers/add_endangered_worker.rb

      В этом файле мы добавим код, который позволит нам работать с данными в нашем файле CSV. Вначале добавьте код в файл, который будет создавать класс, включите в него библиотеку Ruby CSV и убедитесь, что этот класс функционирует как рабочий Sidekiq:

      ~/rails-sidekiq/app/workers/add_endangered_worker.rb

      class AddEndangeredWorker
        require 'csv'
        include Sidekiq::Worker
        sidekiq_options retry: false
      
      end
      

      Также мы добавим опцию retry: false, чтобы Sidekiq не пытался повторить выгрузку в случае ошибки.

      Далее добавьте код для функции perform:

      ~/rails-sidekiq/app/workers/add_endangered_worker.rb

      class AddEndangeredWorker
        require 'csv'
        include Sidekiq::Worker
        sidekiq_options retry: false
      
        def perform(csv_file)
          CSV.foreach(csv_file, headers: true) do |shark|
          Endangered.create(name: shark[0], iucn: shark[1])
        end
       end
      
      end
      

      Метод perform получает аргументы от метода perform_async, определенного в контроллере, поэтому очень важно обеспечить согласованность значений аргументов. Здесь мы передаем файл csv_file и определенную в контроллере переменную, а также используем метод foreach из библиотеки CSV для чтения значений в файле. Установка headers: true для этого цикла обеспечивает обработку первой строки в файле как строки заголовков.

      Затем блок считывает значения из файла в столбцы, заданные для нашей модели Endangered: name и iucn. При выполнении этого цикла будут созданы экземпляры Endangered для каждой из записей в нашем файле CSV.

      После завершения редактирования сохраните и закройте файл.

      Далее мы создадим рабочего для обработки удаления этих данных. Откройте файл для класса RemoveEndangeredWorker:

      • nano app/workers/remove_endangered_worker.rb

      Добавьте код для определения класса и убедитесь, что он использует библиотеку CSV и функционирует как рабочий Sidekiq:

      ~/rails-sidekiq/app/workers/remove_endangered_worker.rb

      class RemoveEndangeredWorker
        include Sidekiq::Worker
        sidekiq_options retry: false
      
      end
      

      Затем добавьте метод perform для обработки уничтожения данных о находящихся под угрозой акулах:

      ~/rails-sidekiq/app/workers/remove_endangered_worker.rb

      class RemoveEndangeredWorker
        include Sidekiq::Worker
        sidekiq_options retry: false
      
        def perform
          Endangered.destroy_all
        end
      
      end
      

      Метод perform вызывает destroy_all для класса Endangered, в результате чего все экземпляры этого класса удаляются из базы данных.

      Сохраните и закройте файл после завершения редактирования.

      Мы настроили рабочих и теперь можем перейти к созданию макета для представлений endangered и шаблонов для представлений index и data, чтобы пользователи могли выгружать и просматривать находящихся под угрозой акул.

      Шаг 4 — Добавление макетов и шаблонов представлений

      Чтобы пользователи смогли использовать свою информацию о находящихся под угрозой акулах, нам нужно обеспечить две вещи: макет представлений, определенных в контроллере endangered, и шаблоны для представлений index и data.

      Сейчас наше приложение использует общий макет app/views/layouts/application.html.erb, элемент навигации и макет для представлений sharks. Макет приложения проверяет наличие блока контента, что позволяет загружать другие макеты в зависимости от того, с какой частью приложения взаимодействует пользователь: для главного указателя они увидят один макет, а для представлений отдельных акул — другой.

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

      Скопируйте файл макета sharks для создания макета endangered:

      • cp app/views/layouts/sharks.html.erb app/views/layouts/endangered.html.erb

      Далее мы создадим шаблоны для представлений index и data.

      Откройте шаблон index:

      • nano app/views/endangered/index.html.erb

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

      ~/rails-sidekiq/app/views/endangered/index.html.erb

      <p id="notice"><%= notice %></p>
      
      <h1>Endangered Sharks</h1>
      
      <p>International Union for Conservation of Nature (ICUN) statuses: <b>vu:</b> Vulnerable, <b>en:</b> Endangered, <b>cr:</b> Critically Endangered </p>
      
      <br>
      
        <%= form_tag endangered_upload_path do %>
        <%= submit_tag "Import Endangered Sharks" %>
        <% end %>
      
        <br>
      
      <%= link_to 'New Shark', new_shark_path, :class => "btn btn-primary btn-sm" %> <%= link_to 'Home', home_index_path, :class => "btn btn-primary btn-sm" %>
      

      Тег form_tag позволяет выгружать данные, указывая действие post для маршрута endangered_upload_path, заданного нами ранее для выгружаемых файлов. Кнопка отправки, созданная с помощью тега submit_tag, предлагает пользователям выполнить «Импорт находящихся под угрозой акул».

      В дополнение к этому коду мы указываем информацию о кодах МСОП, чтобы пользователи могли интерпретировать данные, которые они видят.

      Сохраните и закройте файл после завершения редактирования.

      Откройте файл для представления data:

      • nano app/views/endangered/data.html.erb

      Добавьте следующий код, который добавляет таблицу с данными о находящихся под угрозой видах акул:

      ~/rails-sidekiq/app/views/endangered/data.html.erb

      <p id="notice"><%= notice %></p>
      
      <h1>Endangered Sharks</h1>
      
      <p>International Union for Conservation of Nature (ICUN) statuses: <b>vu:</b> Vulnerable, <b>en:</b> Endangered, <b>cr:</b> Critically Endangered </p>
      
      <div class="table-responsive">
      <table class="table table-striped table-dark">
        <thead>
          <tr>
            <th>Name</th>
            <th>IUCN Status</th>
            <th colspan="3"></th>
          </tr>
        </thead>
      
        <tbody>
          <% @endangered.each do |shark| %>
            <tr>
              <td><%= shark.name %></td>
              <td><%= shark.iucn %></td>
            </tr>
          <% end %>
        </tbody>
      </table>
      </div>
      
      <br>
      
        <%= form_tag endangered_destroy_path do %>
        <%= submit_tag "Delete Endangered Sharks" %>
        <% end %>
      
        <br>
      
      <%= link_to 'New Shark', new_shark_path, :class => "btn btn-primary btn-sm" %> <%= link_to 'Home', home_index_path, :class => "btn btn-primary btn-sm" %>
      

      Этот код содержит коды состояния МСОП и таблицу Bootstrap для выводимых данных. Выполняя цикл с переменной @endangered, мы выведем в таблицу названия и статусы МСОП для всех акул.

      Под таблицей располагается еще один набор тегов form_tags и submit_tags, которые используют метод post для пути destroy, предлагая пользователям возможность «Удалить находящихся под угрозой акул».

      Сохраните и закройте файл после завершения редактирования.

      В заключение мы внесем изменение в представление index, связанное с нашим контроллером home. Возможно вы помните, что это представление было задано как корневое для приложения в config/routes.rb.

      Откройте этот файл для редактирования:

      • nano app/views/home/index.html.erb

      Найдите столбец в строке Sharks are ancient​​​:

      ~/rails-sidekiq/app/views/home/index.html.erb

      . . .
              <div class="col-lg-6">
                  <h3>Sharks are ancient</h3>
                  <p>There is evidence to suggest that sharks lived up to 400 million years ago.
                  </p>
              </div>
          </div>
      </div>
      

      Добавьте в файл следующий код:

      ~/rails-sidekiq/app/views/home/index.html.erb

      . . .
              <div class="col-lg-6">
                  <h3>Sharks are ancient and SOME are in danger</h3>
                  <p>There is evidence to suggest that sharks lived up to 400 million years ago. Without our help, some could disappear soon.</p>
                  <p><%= button_to 'Which Sharks Are in Danger?', endangered_index_path, :method => :get,  :class => "btn btn-primary btn-sm"%>
                  </p>
              </div>
          </div>
      </div>
      

      Мы добавили призыв к действию для пользователей, желающих узнать больше о находящихся под угрозой акулах. Вначале мы разместили привлекающее внимание сообщение, а затем добавили помощник button_to, который отправляет запрос GET на наш маршрут endangered index, предоставляя пользователям доступ к этой части приложения. Там они смогут выгружать и просматривать данные о находящихся под угрозой акулах.

      Сохраните и закройте файл после завершения редактирования.

      Мы разместили код и теперь можем запустить приложение и выгрузить несколько акул!

      Шаг 5 — Запуск Sidekiq и тестирование приложения

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

      Проверьте работу Redis:

      Результат будет выглядеть следующим образом:

      Output

      ● redis-server.service - Advanced key-value store Loaded: loaded (/lib/systemd/system/redis-server.service; enabled; vendor preset: enabled) Active: active (running) since Tue 2019-11-12 20:37:13 UTC; 1 weeks 0 days ago

      Затем проведите миграцию базы данных:

      Теперь вы можете запустить Sidekiq в контексте текущего проекта с помощью команды bundle exec sidekiq:

      Вы увидите следующий вывод, указывающий, что Sidekiq готов к обработке заданий:

      Output

      m, `$b .ss, $$: .,d$ `$$P,d$P' .,md$P"' ,$$$$$b/md$$$P^' .d$$$$$$/$$$P' $$^' `"/$$$' ____ _ _ _ _ $: ,$$: / ___|(_) __| | ___| | _(_) __ _ `b :$$ ___ | |/ _` |/ _ |/ / |/ _` | $$: ___) | | (_| | __/ <| | (_| | $$ |____/|_|__,_|___|_|__|__, | .d$$ |_| 2019-11-19T21:43:00.540Z pid=17621 tid=gpiqiesdl INFO: Running in ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux] 2019-11-19T21:43:00.540Z pid=17621 tid=gpiqiesdl INFO: See LICENSE and the LGPL-3.0 for licensing details. 2019-11-19T21:43:00.540Z pid=17621 tid=gpiqiesdl INFO: Upgrade to Sidekiq Pro for more features and support: http://sidekiq.org 2019-11-19T21:43:00.540Z pid=17621 tid=gpiqiesdl INFO: Booting Sidekiq 6.0.3 with redis options {:id=>"Sidekiq-server-PID-17621", :url=>nil} 2019-11-19T21:43:00.543Z pid=17621 tid=gpiqiesdl INFO: Starting processing, hit Ctrl-C to stop

      Откройте второе окно терминала, перейдите в директорию rails-sidekiq и запустите свой сервер приложений.

      Если вы запускаете приложение на локальном компьютере, используйте следующую команду:

      Если вы работаете с сервером разработки, запустите следующую команду:

      • bundle exec rails s --binding=your_server_ip

      Откройте в браузере адрес localhost:3000 или http://your_server_ip:3000. Вы увидите следующую начальную страницу:

      Домашняя директория приложения Sidekiq

      Нажмите кнопку Which Sharks Are in Danger?. Поскольку вы еще не выгрузили акул, находящихся под угрозой вымирания, сейчас откроется экран endangered index:

      Экран Endangered Index

      Нажмите Import Endangered Sharks для импорта акул. Вы увидите сообщение о состоянии, где будет указано, что акулы были импортированы:

      Начало импорта

      Также вы увидите начало импорта. Обновите страницу, чтобы увидеть таблицу целиком:

      Обновление таблицы

      Благодаря Sidekiq пакетная выгрузка акул, находящихся под угрозой вымирания, была проведена успешно без блокировки браузера и без помех для работы других приложений.

      Нажмите кнопку Home внизу страницы, чтобы вернуться на главную страницу приложения:

      Домашняя директория приложения Sidekiq

      Далее нажмите Which Sharks Are in Danger? еще раз. Поскольку вы уже выгрузили акул, после этого откроется представление data.

      Для тестирования функции удаления нажмите кнопку Delete Endangered Sharks под таблицей. Вы снова должны быть перенаправлены на главную страницу приложения. При нажатии Which Sharks Are in Danger? вы вернетесь к представлению index, где сможете снова выгрузить акул:

      Экран Endangered Index

      Теперь ваше приложение работает с рабочими Sidekiq, которые готовы обрабатывать задания и обеспечивать высокое качество обслуживания для пользователей вашего приложения.

      Заключение

      Мы создали работающее приложение Rails с активированным Sidekiq, позволяющее разгружать ресурсоемкие операции в очередь заданий под управлением Sidekiq при поддержке Redis. Это позволит повысить скорость и функциональность разрабатываемого сайта.

      Если вы хотите узнать больше о Sidekiq, начните с документации.

      Дополнительную информацию о Redis можно найти в нашей библиотеке ресурсов по Redis. Дополнительную информацию о запуске управляемого кластера Redis в инфраструктуре DigitalOcean можно найти в документации по продукту.



      Source link