Всем привет!
Аутентификация пользователей уже давно является типовой задачей. В Django, как и в любом современном вэб-фреймворке, есть отличный механизм аутентификации пользователей.
Однако, этот механизм по умолчанию использует логин в качестве идентификатора, в то время как все мы уже привыкли использовать для входа email.
Когда мне понадобилось реализовать этот функционал, оказалось что существует не так много туториалов, особенно на русском, в которых бы описывалось как сделать регистрацию по email, отправку верифицирующего письма, сброс пароля и другие, в общем то вполне обычные вещи.
Я решил исправить эту несправедливость.

Покопавшись немного в интернетах, мой выбор пал на модуль django-user-accounts.
Этот модуль — часть экосистемы Pinax, имеет неплохую документацию и, что самое приятное, немного легко-читаемого кода.
Модуль умеет:
- Регистрировать пользователя по Email;
- Отправлять письмо с подтверждающей ссылокой;
- Аутентифицировать пользователя при помощи Email и пароля;
- Изменять пароль из интерфейса;
- Сбрасывать и восстанавливать пароль;
- Отслеживать "протухание" пароля;
- Изменять параметры аккаунта (например локаль или часовой пояс);
- Удалять аккаунт.
Инсталяция
Для установки выполняем команду:
pip install django-user-accounts
Не забываем добавить зависимость в файл requirements.txt:
django-user-accounts==2.0.3
В settings.py добавляем INSTALLED_APPS:
INSTALLED_APPS =[ ..... 'django.contrib.sites', 'account' ]
Важно добавить стандартный джанговский модуль sites, так как account от него зависит.
Дальше добавляем MIDDLEWARE_CLASSES:
MIDDLEWARE_CLASSES = [ ..... 'account.middleware.LocaleMiddleware', 'account.middleware.TimezoneMiddleware' ]
И context_processors:
TEMPLATES = [ { ..... 'OPTIONS': { 'context_processors': [ ..... 'account.context_processors.account' ], }, }, ]
Для того, чтобы у нас email был уникальный и чтобы требовалось подтвеждение добавим два ключа в settings.py:
ACCOUNT_EMAIL_UNIQUE = True ACCOUNT_EMAIL_CONFIRMATION_REQUIRED = True
Теперь можно выполнить миграции:
python manage.py migrate
В результате в БД появятся новые таблицы:
account_account account_accountdeletion account_emailaddress account_emailconfirmation account_passwordexpiry account_passwordhistory account_signupcode account_signupcoderesult django_site
Если у вас ранее не было подключенного модуля sites, то нужно создать сайт:
python manage.py shell >>> from django.contrib.sites.models import Site >>> site = Site(domain='localhost:8000', name='localhost:8000') >>> site.save() >>> site.id 2
И добавить в setting.py id нового сайта:
SITE_ID = 2
Все templates для необходимых страниц и текстов писем можно скачать из pinax-theme-bootstrap и просто поместить их по адресу yourproject/yourapp/templates/account.
Настройка
Если вы собираетесь подключать этот модуль на уровне Django-приложения, а не проекта, то для корректной работы маршрутизаций, добавим в settings.py следующие строки:
ACCOUNT_LOGIN_URL = 'yourapp:account_login' ACCOUNT_EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL = ACCOUNT_LOGIN_URL ACCOUNT_PASSWORD_RESET_REDIRECT_URL = ACCOUNT_LOGIN_URL ACCOUNT_EMAIL_CONFIRMATION_URL = "yourapp:account_confirm_email" ACCOUNT_SETTINGS_REDIRECT_URL = 'yourapp:account_settings' ACCOUNT_PASSWORD_CHANGE_REDIRECT_URL = "yourapp:account_password"
И добавим урлы в файл yourapp/urls.py:
urlpatterns = [ ..... url(r"^account/", include("account.urls")), ..... ]
Теперь доступны следующие адреса:
account/signup/ account/login/ account/logout/ account/confirm_email/<key> account/password/ account/password/reset/ account/password/reset/<token> account/settings/ account/delete/
Осталось лишь добавить настройки для почтового сервера в settings.py:
DEFAULT_FROM_EMAIL = 'support@yoursite.ru' EMAIL_HOST = "smtp.yoursmtpserver.ru" EMAIL_PORT = 25 EMAIL_HOST_USER = "user" EMAIL_HOST_PASSWORD = "pass"
Если вы всё правильно сделали, то по указанным урлам можно логиниться при помощи логина/пароля, а при создании учетки вам на почту будет отправлено письмо для подтверждения email-а.
Лирическое отступление
Была обнаружена бага, которая не позволяет корректно сформировать ссылку на восстановление пароля если вы добавили ссылки в приложение, а не в проект.
Пока не принят пулл-реквест можно вбить костыль в файл yourproject/urls.py
from account.views import PasswordResetTokenView urlpatterns = [ ..... url(r"^account/password/reset/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$", PasswordResetTokenView.as_view(), name="account_password_reset_token"), ..... ]
В пулл-реквесте появится новая настройка:
ACCOUNT_PASSWORD_RESET_TOKEN_URL = 'yourapp:account_password_reset_token'
Аутентификация по Email
Для начала добавляем соответсвующий backend в settings.py:
AUTHENTICATION_BACKENDS = [ 'account.auth_backends.EmailAuthenticationBackend', ]
Затем удаляем из формы регистрации username, для этого в файл yourapp/forms.py добавляем следующее:
import account.forms class SignupForm(account.forms.SignupForm): def __init__(self, *args, **kwargs): super(SignupForm, self).__init__(*args, **kwargs) del self.fields["username"]
А в файл yourapp/views.py это:
import yourapp.forms import account.forms import account.views class LoginView(account.views.LoginView): form_class = account.forms.LoginEmailForm class SignupView(account.views.SignupView): form_class = yourapp.forms.SignupForm def generate_username(self, form): username = form.cleaned_data["email"] return username def after_signup(self, form): # do something super(SignupView, self).after_signup(form)
Здесь мы для вьюшек задаем классы форм: LoginEmailForm и нашу SignupForm соответсвенно, а так же переопределяем метод after_signup, чтобы можно было подмешать в него какое-нибудь нужное нам поведение. По-умолчанию полю username присваивам значение email.
Осталось лишь переопределить наши url-ы в файле yourapp/urls.py:
from . import views urlpatterns = [ ..... url(r"^account/login/$", views.LoginView.as_view(), name="account_login"), url(r"^account/signup/$", views.SignupView.as_view(), name="account_signup"), url(r"^account/", include("account.urls")), ..... ]
Обращаю внимание, что вызов кастомных вьюшек должен идти перед подключением account.urls, иначе они не переопределятся.
Миграция старых данных
Для того чтобы текущие пользователи могли залогиниться нужно добавить их email адреса в таблицу account_emailaddress:
insert into account_emailaddress(email, verified, "primary", user_id) select au.email, True, True, au.id from auth_user au where au.email is not null
В данном случае в поле verified вставляется значение true, т.е. мы сразу их подтверждаем.
Для того, чтобы у всех пользователей появился аккаунт, заполнить таблицу account_account:
insert into account_account (timezone, "language", user_id) select '','ru', id from auth_user
Заключение
Подробнее о модуле django-user-account вы можете узнать в официальной документации.
Исходный код находится тут. Его полезно почитать, чтобы чуть лучше разобраться в том, как работает механизм auth в Django.
Надеюсь, эта статья поможет вам сэкономить время. Просьба поделиться в комментариях, какими инструментами пользуетесь вы.
