Разработка бэкенда Web3 и смарт-контрактов для Python-разработчиков часть 8: Бэкенд Django функциональность логирования и выхода из системы

Разработка бэкенда Web3 и смарт-контрактов для Python-разработчиков часть 8: Бэкенд Django функциональность логирования и выхода из системы

Теперь перейдем к рассмотрению бэкенда Django. Первым шагом, как всегда, является активация соответствующего виртуального окружения Python. Из папки ./musical_nft_thumbnails

$source env/bin/activate

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

Создание нового приложения Django для аутентификации

$py manage.py startapp authentication

В этом вновь созданном приложении мы будем управлять процессом регистрации, входом в систему, выходом из нее и сбросом пароля.

Добавить в musical_nft/settings.py аутентификацию в список установленных приложений:

    INSTALLED_APPS = [
        "django.contrib.admin",
        "django.contrib.auth",
        "django.contrib.contenttypes",
        "django.contrib.sessions",
        "django.contrib.messages",
        "django.contrib.staticfiles",
        # here =>
        "authentication",
]

Теперь в глобальном файле urls.py внутри musical_nft/settings.py добавьте

 from django.contrib import admin
    from django.urls import path, include

    urlpatterns = [
        path("admin/", admin.site.urls),
        path("", include("authentication.urls"))
    ]

Теперь все пути, которые мы имеем внутри приложения аутентификации, будут подключены к этому проекту urls.py

В папке приложения аутентификации создайте еще один urls.py и передайте в него следующий код

   from django.urls import path
    from .views import HomeView

    urlpatterns = [
        path("", HomeView.as_view(), name="home"),
    ]

Если вы не создали суперпользователя, давайте сделаем это сейчас

$python manage.py createsuperuser

Теперь откройте браузер http:127.0.0.1:8000/admin при запущенном локальном Django-сервере и введите учетные данные суперпользователя (обязательно убедитесь, что ваш postgres-сервер запущен $sudo service postgresql start).

Описание изображения

После того как вы вошли в систему, перейдите в раздел ”Пользователь” и добавьте нового пользователя (подтвердите пароль)

Описание изображения

На данном этапе нам нужен класс представления аутентификации и несколько html-страниц для рендеринга. При создании приложений Django следует помнить, что это всегда трехступенчатый процесс: определение адресов (в приложении), представлений (в нашем случае представлений на основе классов. Если вы хотите поэкспериментировать с альтернативными реализациями этого кода, то можно сделать представление на основе функций) и шаблоны (html-страница, которая будет отображаться с небольшим количеством добавленных Django-элементов).

В файл views.py приложения аутентификации добавьте следующий код:

from django.shortcuts import render
    from django.views.generic import TemplateView

    class HomeView(TemplateView):

        def get(self, request):
            return render(request, "home.html", {})

Только для тестирования создадим простой файл home.html с сообщением, которое будет отображаться в браузере. Для этого создадим в ./muscial_nft_thumbnails/tempaltes/home.html

 {% extends "base.html" %}

    {% block content%}
        <h1> Test screen </h1>
    {% endblock content%}

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

Затем в settings.py добавьте определение корневой папки шаблона

TEMPLATES = [
        {
            "BACKEND": "django.template.backends.django.DjangoTemplates",
            "DIRS": [os.path.join(BASE_DIR, "templates")],
            "APP_DIRS": True,
            "OPTIONS": {
                "context_processors": [
                    "django.template.context_processors.debug",
                    "django.template.context_processors.request",
                    "django.contrib.auth.context_processors.auth",
                    "django.contrib.messages.context_processors.messages",
                ],
            },
        },
    ]

В этом проекте мы будем использовать bootstrap для стилизации. Перейдите на страницу их документации и возьмите из введения второй вариант номер два, Include Bootstrap’s CSS and JS. и вставьте этот код во вновь созданный файл base.html в корневой папке проекта ./templates (или просто скопируйте отсюда)

<!doctype html>
    <html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Muscial NFT</title>
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
    </head>
    <body>
        <div class="container ">       
            <br/>
            <br/>
            {% block content %}
            {% endblock content %}
        </div>
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
    </body>
    </html>

Теперь добавим navbar на нашу страницу base.html.

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

Вот отредактированный код, который нужно передать в файл navbar.html в папке ./templates в корневой директории проекта.

<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
        <div class="container-fluid">
        <a class="navbar-brand" href="https://dev.to/ilija/{% url"home' %}">Musical NFT </a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarSupportedContent">
            <ul class="navbar-nav me-auto mb-2 mb-lg-0">
            <li class="nav-item">
                <a class="nav-link" href="#">Link</a>
            </li>
                </ul>
            </li>
            </ul>
        </div>
        </div>
    </nav>

А затем просто перейдите в ранее созданный файл base.html и добавьте одну новую строку после первого тега body

{% include “navbar.html”%}

Из корневого каталога

$py manage.py runserver

Перейдите в браузер по адресу http://127.0.0.1:8000 и проверьте, появилось ли на экране правильное сообщение.

Теперь, если вы вернетесь в браузер, вы должны увидеть что-то вроде этого

Описание изображения

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

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

Описание изображения

Сначала мы доработаем 3 основных шаблона: navbar, base и home (base включает в себя navbar, а home расширяет base, как показывает поток). Следует иметь в виду, что у home будет две возможности: 1) если пользователь залогинен, то мы будем показывать ему его персональную страницу; 2) если пользователь не залогинен, то мы будем предлагать ему формы входа (это можно проверить в файле home.html).

navbar.html (модифицированный bootstrap navbar)

<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
        <div class="container-fluid">
        <a class="navbar-brand" href="https://dev.to/ilija/{% url"home' %}">Musical NFT </a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarSupportedContent">
            <ul class="navbar-nav me-auto mb-2 mb-lg-0">
            {% if user.is_authenticated%}
            <li class="nav-item">
            <a class="nav-link" href="https://dev.to/ilija/{% url"logout'%}">Logout</a>
            </li>
            {% else %}
            <li class="nav-item">
            <a class="nav-link" href="https://dev.to/ilija/{% url"home'%}">Login</a>
            </li>
            {% endif%}
                </ul>
            </li>
            </ul>
        </div>
        </div>
    </nav>

Теперь base.html включает в себя navbar.html

<!doctype html>
    <html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Muscial NFT</title>
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
    </head>
    <body>
        {% include "navbar.html"%}
        <div class="container ">       
            <br/>
            <br/>
            {% if messages %}
            {% for message in messages%}
            <div class="alert alert-warning alert-dismissible fade show" role="alert">
            {{ message }}
            <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
            </div>

            {% endfor%}
            {% endif %}

            {% block content %}

            {% endblock content %}
        </div>
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
    </body>
    </html>

Здесь я хочу обратить ваше внимание на последний {% block content %} {% endblock content %} Это место, где base.html инжектирует любой другой шаблон, который наследует от него с помощью команды extends (в нашем случае home.html).

Далее несколькими строками выше вы имеете немного странный синтаксис с {% if messages %} {% for message in messages%} Просто таким образом мы передаем в наш шаблон сообщение об успехе или ошибке. Более подробную информацию об этом можно найти в файле view.py.

Как мы видим, home.html будет расширять base.html и иметь внутри себя специфические для Django статусы if/else. Эта строка if/else поможет нам проверить, вошел ли пользователь в систему, после чего мы предложим ему его персональную страницу. В случае если пользователь не вошел в систему, мы покажем ему форму входа.

Ниже приведен пример кода:

 {% extends "base.html" %}

    {% block content%}

    <div class="col-md-6 offset-md-3"> 

        {% if user.is_authenticated %} 
        <h1> Welcome <h1/>
        {% else %}
        <h1> Login </h1>
        <br/>
        <form method="POST" action="https://dev.to/ilija/{% url"home' %}"> 
            {% csrf_token %}
            <form>
                <div class="mb-3">
                <input type="text" class="form-control" aria-describedby="emailHelp" placeholder="Username" name="username" required>
                </div>
                <div class="mb-3">
                <input type="password" class="form-control" placeholder="Password" name="password" required>
                </div>
                <button type="submit" class="btn btn-secondary">Submit</button>
            </form>
            </form> 
    </div>

    {% endif %}
    {% endblock content%}

Теперь у нас есть все шаблоны, и мы можем перейти в папку authentication app, где нам нужно обновить наш urls.py следующим кодом

    from django.urls import path
    from .views import HomeView, LogoutUser

    urlpatterns = [
        path("", HomeView.as_view(), name="home"),
        # path("login/", LoginUser.as_view(), name="login"),
        path("logout/", LogoutUser.as_view(), name="logout"),
    ]

Как видно, мы добавили новый путь выхода из системы (логин является частью обычного пути home и класса HomeView), который будет обрабатываться классом LogoutUser внутри authenticaiotn view.py.

Как вы уже догадались, последний кусочек паззла, который нам нужен, это обработчик просмотра, который склеит все эти элементы вместе. Вот пример возможного кода:

from django.shortcuts import render, redirect
    from django.views.generic import TemplateView
    from django.contrib.auth import authenticate, login, logout
    from django.contrib import messages



    class HomeView(TemplateView):
        template_name="home.html"

        def post(self, request):
            # check to see if loggin in
            if request.method == "POST":
                user_name = request.POST["username"]
                password = request.POST["password"]
                user = authenticate(request, username=user_name, password=password)
                if user is not None:
                    login(request, user)
                    messages.success(request, "You have been logged in!")
                    return redirect("home")    
                else:
                    messages.success(request, "There was An Error login in, please try again")
                    return redirect("home")  
            else:
                return render(request, "home.html", {})


    class LogoutUser(TemplateView):    
        def get(self, request):
            logout(request)
            messages.success(request, "You have been logged out!")
            return redirect("home")  

В этот момент запустите ваш Django-сервер локально (убедитесь, что ваш postgres-сервер работает и запущен), а затем перейдите по адресу http://127.0.0.1:8000. Если вы вошли в систему как администратор, вы должны увидеть что-то вроде этого

Описание изображения

Теперь введите учетные данные администратора или только что созданного пользователя, и вы должны увидеть что-то вроде этого =>

Описание изображения

Посмотрите на кнопку выхода из системы в левом верхнем углу навигационной панели. Эта кнопка попадет на наш путь logout/, а затем на обработчик класса LogoutUser (из views.py), который просто выйдет из системы и перенаправит текущего пользователя на домашнюю страницу.

И на этом месте мы, наконец, можем увидеть очень красивый момент в файле home.html. Этот цикл внутри home.html увидит, что пользователь уже не вошел в систему, и покажет ему формат логина. И, в общем-то, благодаря этой приятной детали у нас все в одном месте: и вход, и выход, и профиль пользователя - все прекрасно связано в этом шаблонном цикле.

Описание изображения

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

Код для этого можно найти на github