К данному моменту мы создали блог на Django, который использует формы для создания, редактирования и удаления статей, однако главный элемент большинства веб-сайтов все еще отсутствует: аутентификация пользователя.
Содержание статьи
- Аутентификация пользователя в Django (LoginView)
- Данные пользователя на главной странице | is_authenticated
- Выход пользователя из профиля
- Регистрация нового пользователя в Django
- Загружаем изменения на Github
- Настраиваем Heroku для Блога на Django
- Запускаем Django Блог на Heroku
Качественную и безопасную реализацию аутентификации пользователя в Django осуществить довольно сложно. По ходу процесса, то и дело будут всплывать новые проблемы, связанные с безопасностью, самостоятельно справиться с которыми новичку не под силу. К счастью, в Django есть встроенная система аутентификации пользователя, которую мы сейчас будем использовать.
Есть вопросы по Python?
На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!
Паблик VK
Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!
При создании любого нового проекта, Django по умолчанию устанавливает приложение auth
. В нем есть объект User, содержащий такие поля как:
- username;
- password;
- email;
- first_name;
- last_name.
Мы используем объект User
для реализации входа, выхода и регистрации пользователя в блоге на Django.
Аутентификация пользователя в Django (LoginView)
По умолчанию, Django поставляется с представлением LoginView для страницы входа. Нам нужно только настроить:
- URL маршруты для системы аутентификации;
- создать HTML шаблон для страницы входа;
- обновить файл
settings.py
.
Для начала обновим файл blog_project/urls.py
. Поместим страницы для входа и для выхода на URL префиксе accounts
. Небольшое добавление на предпоследней строке.
1 2 3 4 5 6 7 8 9 |
# blog_project/urls.py from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('accounts/', include('django.contrib.auth.urls')), # Добавили новый маршрут path('', include('blog.urls')), ] |
Как отмечается в документации к LoginView, по умолчанию Django будет искать файл login.html
в папке registration
. Таким образом, нам нужно создать новую директорию под названием registration
, а внутри нее создать необходимый HTML файл шаблона. Закройте локальный веб-сервера, используя в командной строке комбинацию Control+c
. После этого введите следующее команды:
1 2 |
(blog) $ mkdir templates/registration (blog) $ touch templates/registration/login.html |
Теперь наполните файл следующим контентом:
1 2 3 4 5 6 7 8 9 |
<!-- templates/registration/login.html --> {% extends 'base.html' %} {% block content %} <h2>Log In</h2> <form method="post"> {% csrf_token %} {{ form.as_p }} <button type="submit">Log In</button> </form> {% endblock content %} |
Мы используем HTML теги для формы <form></form>
и указываем POST как метод отправки, так как здесь нужно будет отправить данные на сервер (в случае, если понадобилось запросить данные, например во время поиска, мы бы использовали метод GET). Добавляем в форму токен {% csrf_token %}
в целях безопасности — по большей части для предотвращения XSS аттак. Содержимое формы выводится между тегами параграфа при помощи {{ form.as_p }}
, после чего добавляется кнопка «Log In».
На финальном этапе нам нужно уточнить, куда именно перенаправить пользователя после успешного входа. Для этого настроем переменную LOGIN_REDIRECT_URL
. В нижней части файла settings.py
добавляем следующее:
1 2 |
# blog_project/settings.py LOGIN_REDIRECT_URL = 'home' |
Теперь пользователь будет направлен на URL-маршрут который имеет название 'home'
, который является домашней страницей.
Все действительно получилось! При новом запуске локального веб-сервера через команду python manage.py runserver
и последующем переходе на страницу входа http://127.0.0.1:8000/accounts/login/ откроется следующее:
Страница аутентификации для пользователя в Django
После ввода логина и пароля нашего аккаунта мы будем направлены на домашнюю страницу. Обратите внимание, что мы не добавляли никаких логических операции для представления и не создавали модели базы данных. Система аутентификации Django сама предоставляет все необходимое. Спасибо, Django!
Данные пользователя на главной странице | is_authenticated
Обновим шаблон base.html
таким образом, чтобы пользователи могли увидеть сообщение вне зависимости от того, прошли они аутентификацию или нет. Для этого будем использовать атрибут is_authenticated.
Теперь нужно просто разместить следующий код в нужном месте нашего шаблона. Обновим файл base.html
, вставив новый код под закрывающимся тегом </header>
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<!-- templates/base.html --> {% load static %} <html> <head> <title>Django blog</title> <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:400" rel="stylesheet"> <link href="{% static 'css/base.css' %}" rel="stylesheet"> </head> <body> <div> <header> <div class="nav-left"> <h1><a href="{% url 'home' %}">Django blog</a></h1> </div> <div class="nav-right"> <a href="{% url 'post_new' %}">+ New Blog Post</a> </div> </header> {% if user.is_authenticated %} <p>Hi {{ user.username }}!</p> {% else %} <p>You are not logged in.</p> <a href="{% url 'login' %}">Log In</a> {% endif %} {% block content %} {% endblock content %} </div> </body> </html> |
После аутентификации, система поприветствует пользователя. В противном случае вы будете перенаправлены на страницу аутентификации.
Домашняя страница после аутентификации
Все сработало! Мое имя суперпользователя — wsv
. Именно его я вижу на главной странице.
Выход пользователя из профиля на Django
Пользователь успешно прошел процедуру аутентификации, но… как теперь выйти? Можно было бы зайти в админку и выйти оттуда, однако есть способ получше. Добавим ссылку выхода, которая будет перенаправлять человека на домашнюю страницу. Благодаря системе аутентификации Django, добиться такого сценария проще простого.
В файле шаблона base.html
добавим ссылку {% url 'logout' %}
для выхода сразу после приветствия пользователя.
1 2 3 4 5 6 7 |
<!-- templates/base.html--> ... {% if user.is_authenticated %} <p>Hi {{ user.username }}!</p> <p><a href="{% url 'logout' %}">Log out</a></p> {% else %} ... |
Вот и все, что от нас требуется, ведь само представление предоставляется приложением auth
от Django. Правда, нам нужно уточнить, куда именно пользователь будет направлен после выхода из профиля.
Обновим settings.py
для создания ссылки перенаправления, которая будет называться LOGOUT_REDIRECT_URL
. Мы можем добавить ее рядом с ссылкой для входа, таким образом файл должен выглядеть следующим образом:
1 2 3 |
# blog_project/settings.py LOGIN_REDIRECT_URL = 'home' LOGOUT_REDIRECT_URL = 'home' # Новое изменение |
При обновлении домашней страницы можно увидеть, что у вошедших пользователей появилась ссылка для выхода «Log out«.
Домашняя страница с ссылкой для выхода
После нажатия на появившуюся ссылку, вы будете перенаправлены на домашнюю страницу, где покажется ссылка для входа.
Домашняя страница вышедшего пользователя
Попробуйте зайти и выйти несколько раз с вашим аккаунтом.
Регистрация нового пользователя в Django
Для регистрации новых пользователей также понадобится представление (views). В Django для этого предусмотрен класс формы UserCreationForm, который сильно упрощает работу. По умолчанию в нем присутствую три поля: username
, password
и password2
.
Для создания надежной системы аутентификации предусмотрено множество способов организации кода и URL маршрутов. Сейчас мы создадим новое приложение accounts
, специально для страницы регистрации.
1 |
(blog) $ python manage.py startapp accounts |
Затем новое приложение добавляется в переменную INSTALLED_APPS
из файла settings.py
.
1 2 3 4 5 6 7 8 9 10 11 |
# blog_project/settings.py INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'blog.apps.BlogConfig', 'accounts.apps.AccountsConfig', # Добавляем приложение тут ] |
Далее добавляем новый URL маршрут в blog_project/urls.py
, указывая на новое приложение прямо под тем местом, где мы включили приложение auth
.
1 2 3 4 5 6 7 8 9 10 |
# blog_project/urls.py from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('accounts/', include('django.contrib.auth.urls')), path('accounts/', include('accounts.urls')), # новое добавление path('', include('blog.urls')), ] |
Порядок URL маршрутов из переменной urlpatterns
имеет значение, так как Django читает файл сверху вниз. Следовательно, когда создается запрос на URL-адрес /accounts/signup
, Django вначале будет искать в django.contrib.auth.urls
, и, не найдя такой, перейдет к приложению accounts.urls
.
Двигаемся далее и создаем файл accounts/urls.py
.
1 |
(blog) $ touch accounts/urls.py |
И добавляем в него следующий код:
1 2 3 4 5 6 7 8 |
# accounts/urls.py from django.urls import path from .views import SignUpView urlpatterns = [ path('signup/', SignUpView.as_view(), name='signup'), ] |
Мы используем пока не созданное представление под названием SignUpView
, которое, если судить по наличию заглавных букв, является классовым представлением и имеет метод as_view()
. Его URL-маршрут просто signup/
, так что полный URL-маршрут будет accounts/signup/
.
Теперь перейдем к представлению, что использует встроенный класс UserCreationForm
, и общий класс представления CreateView
.
1 2 3 4 5 6 7 8 9 10 |
# accounts/views.py from django.contrib.auth.forms import UserCreationForm from django.urls import reverse_lazy from django.views import generic class SignUpView(generic.CreateView): form_class = UserCreationForm success_url = reverse_lazy('login') template_name = 'signup.html' |
Общий класс представления (generic) CreateView
наследуется в классе SignUpView
. Мы уточняем использование встроенного UserCreationForm
и пока еще не созданного шаблона signup.html
. Используем функцию reverse_lazy
для перенаправления пользователя на страницу входа после успешной регистрации.
Зачем использовать
reverse_lazy
вместоreverse
? Причина в том, что для всех общих классовых представлений URL не загружаются, когда имортируется файл. Поэтому нам нужно использовать функциюreverse_lazy
для последующей загрузки URL, когда они будут доступны.
Теперь создадим HTML файл шаблона signup.html
в нашу директорию templates
:
1 |
(blog) $ touch templates/signup.html |
После создания заполняем файл следующим кодом:
1 2 3 4 5 6 7 8 9 10 11 |
<!-- templates/signup.html --> {% extends 'base.html' %} {% block content %} <h2>Sign up</h2> <form method="post"> {% csrf_token %} {{ form.as_p }} <button type="submit">Sign up</button> </form> {% endblock content %} |
Формат очень похож на тот, что мы делали ранее. В верхней части мы расширяем базовый шаблон base.html
, помещаем логические операции между тегами <form></form>
, используем csrf_token
в целях безопасности, отображаем содержимое формы используя form.as_p
и добавляем кнопку подтверждения «Sign Up».
Все готово! Для проверки запустите локальный веб-сервер через python manage.py runserver
и перейдите на недавно созданную страницу http://127.0.0.1:8000/accounts/signup/:
Страница регистрации нового пользователя в Django
Обратите внимание на большое количество текста, что Django включает по умолчанию. Мы можем убрать его через настройки встроенного фреймворка сообщений, но этого делать не будем и просто воспользуемся уже готовой формой.
Мы создали нового пользователя с именем «william» и после регистрации нас перенаправили на страницу входа. После успешного ввода имени и пароля мы были направлен на персонализированную домашнюю страницу с приветствием в формате «Hi {username}».
Домашняя страница пользователя william
Общий порядок действий можно представить, как: Регистрация -> Аутентификация -> Домашняя страница
. Конечно, мы можем внести сюда любые поправки. SignUpView
перенаправляет на URL-маршрут с названием login
, так как мы установили success_url = reverse_lazy('login')
. Страница аутентификации перенаправляет на домашнюю страницу, так как в файле blog_project/settings.py
мы установили LOGIN_REDIRECT_URL = 'home'
.
Поначалу многообразие путей развития проекта Django может запутать. Это нормально. Со временем все выстроится в логичную цепочку действий.
Загружаем изменения на Github
С нашего последнего git коммита прошло много времени. Давайте выполним коммит, а затем загрузим копию кода на GitHub. Для начала посмотрим на всю выполненную работу через команду git status
.
1 |
(blog) $ git status |
Добавляем все изменения в очередь на загрузку и описываем коротко, что из себя представляет данный коммит.
1 2 |
(blog) $ git add -A (blog) $ git commit -m 'forms and user accounts' |
Создайте новое хранилище на GitHub, назвав его по своему усмотрению. Я выбрал название blog-app
. После создания нового хранилища на GitHub можно выполнить следующие две команды. Не забудьте поменять в коде мое имя пользователя stillriverpress
на свой никнейм.
1 2 |
(blog) $ git remote add origin https://github.com/stillriverpress/blog-app.git (blog) $ git push -u origin master |
Все готово! Теперь можно разместить новое приложение на Heroku.
Настраиваем Heroku для Блога на Django
Сейчас мы опубликуем приложение на Heroku уже в третий раз. Как и в случае с приложением доски объявления на Django, для использования Heroku потребуется сделать четыре изменения в файлах нашего блога.
- обновить файл
Pipfile.lock
- создать новый файл
Procfile
- установить WSGI HTTP сервер
Gunicorn
- обновить файл
settings.py
Уточняем Python версию в Pipfile
, затем выполняем команду pipenv lock
что приведет к созданию файла Pipfile.lock
. Создаем файл Procfile
, который является файлом конфигурации Heroku, устанавливаем gunicorn
в качестве веб-сервера вместо локального веб-сервера Django и в конечном итоге обновляем переменную ALLOWED_HOSTS
, чтобы приложение было видно каждому на просторе интернета.
Откройте файл Pipfile
в текстовом редакторе и в нижней части добавьте две строчки:
1 2 |
[requires] python_version = "3.7" |
Здесь мы указываем версию 3.7
вместо более точного 3.7.3
. Приложение автоматически подстроится под текущую версию Python 3.7.x на Heroku.
Теперь запустите команду pipenv lock
для обновления файла Pipfile.lock
, так как Heroku использует его для генерации виртуального окружения на серверах Heroku для нашего приложения.
1 |
(blog) $ pipenv lock |
Создается новый файл Procfile
.
1 |
(blog) $ touch Procfile |
Через текстовый редактор в Procfile
добавьте следующую строчку:
1 |
web: gunicorn blog_project.wsgi --log-file - |
Таким образом Heroku будет знать, что надо использовать Gunicorn в качестве веб-сервера, а не локальный веб-сервер от Django.
Теперь устанавливаем Gunicorn.
1 |
(blog) $ pipenv install gunicorn==19.9.0 |
Наконец-то можно обновить переменную ALLOWED_HOSTS
для разрешения всех доменов.
1 2 |
# blog_project/settings.py ALLOWED_HOSTS = ['*'] |
Теперь можно закоммитить новые изменения и перенести все на GitHub.
1 2 3 4 |
(blog) $ git status (blog) $ git add -A (blog) $ git commit -m 'Heroku config files and updates' (blog) $ git push -u origin master |
Запускаем Django Блог на Heroku
Для размещения проекта на Heroku сначала нужно подтвердить, что вы действительно вошли в свой аккаунт.
1 |
(blog) $ heroku login |
Затем введите команду create
, которая говорит Heroku, что нужно сделать контейнер, в котором будет работать наше новое приложение. Если выполнить команду heroku create
, то Heroku выдаст вам случайное имя суб-домена. Однако вы также можете настроить свое собственное имя суб-домена, только оно должно быть уникальным на Heroku. Другими словами, ввиду того, что я выбрал название dfb-blog
, теперь оно для вас недоступно. Придется воспользоваться какой-то другой комбинацией букв и цифр.
1 |
(blog) $ heroku create dfb-blog |
Теперь настроим git
, чтобы при размещении на Heroku было присвоено название вашего нового приложения (заменяете dfb-blog
на выбранное вами название).
1 |
(blog) $ heroku git:remote -a dfb-blog |
Остался еще один этап — нужно разобраться со статическими файлами, которые в нашем случае это CSS стили. На этапе продакшена Django не поддерживает оперирование статическими файлами, для этого можно использовать WhiteNoise. Давайте установим его.
1 |
(blog) $ pipenv install whitenoise==4.1.4 |
Далее нам нужно обновить статические настройки для дальнейшей работы в продакшене. Через текстовый редактор откройте файл settings.py
. Добавьте whitenoise
к INSTALLED_APPS
над встроенным приложением staticfiles
, а также в переменой MIDDLEWARE
на третьей строке. Порядок имеет значение как для INSTALLED_APPS
, так и для MIDDLEWARE
.
В нижней части файла добавьте новые строки кода для STATIC_ROOT
и STATICFILES_STORAGE
. Все должно выглядеть следующим образом.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# blog_project/settings.py INSTALLED_APPS = [ 'blog.apps.BlogConfig', 'accounts.apps.AccountsConfig', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'whitenoise.runserver_nostatic', # Добавили тут! 'django.contrib.staticfiles', ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'whitenoise.middleware.WhiteNoiseMiddleware', # Добавили тут! 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ... STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') # Добавили тут! STATIC_URL = '/static/' STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')] STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' |
Не забудьте добавить и закоммитить все новые изменения. Затем обновите проект на GitHub.
1 2 3 |
(blog) $ git add -A (blog) $ git commit -m 'Heroku config' (blog) $ git push origin master |
Наконец-то можно разместить код на Heroku и убедиться в том, что веб-приложение работает должным образом.
1 2 |
(blog) $ git push heroku master (blog) $ heroku ps:scale web=1 |
URL нашего нового приложения будет в выводе командной строки, имя домена можно получить через команду heroku open
. Мой проект расположен на https://dfb-blog.herokuapp.com/.
Блог на Heroku
Заключение
При помощи минимального количества кода, фреймворк Django позволяет нам создать средства для регистрации и аутентификации пользователей. По умолчанию решаются многие проблемы безопасности, которые часто всплывают при самостоятельном написании кода для форм аутентификации с нуля.
Являюсь администратором нескольких порталов по обучению языков программирования Python, Golang и Kotlin. В составе небольшой команды единомышленников, мы занимаемся популяризацией языков программирования на русскоязычную аудиторию. Большая часть статей была адаптирована нами на русский язык и распространяется бесплатно.
E-mail: vasile.buldumac@ati.utm.md
Образование
Universitatea Tehnică a Moldovei (utm.md)
- 2014 — 2018 Технический Университет Молдовы, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Технический Университет Молдовы, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»