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

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

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

Создание веб-сайта с нуля на Django и Bootstrap. Аккаунты. Модель и первые миграции

Сергей Серов

Для начала перейдем в командную строку и создадим приложение с названием accounts:

python manage.py startapp accounts

Модель

Открываем каталог с приложением и наполняем файл models.py:

from django.db import models
from django.contrib.auth.models import UserManager, AbstractUser


class ActiveUsersManager(UserManager):
    def get_queryset(self):
        return super(ActiveUsersManager, self).get_queryset().filter(is_active=True)


class InactiveUsersManager(models.Manager):
    def get_queryset(self):
        return super(InactiveUsersManager, self).get_queryset().filter(is_active=False, last_login__isnull=True)


class User(AbstractUser):
    username = models.CharField('псевдоним или настоящее имя', max_length=255, unique=True)
    email = models.EmailField('адрес электронной почты', blank=True, null=True, unique=True)

    objects = UserManager()
    active = ActiveUsersManager()
    inactive = InactiveUsersManager()

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

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

Ограничения вшиты непосредственно в само поле, поэтому мы его переопределим и заодно выставим максимальную длину имени — 255 символов.

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

Менеджеры

По умолчанию используется менеджер объектов от стандартной модели пользователя. В результирующий набор данных поступят все пользователи без каких-либо ограничений. Например, сторонние приложения не могут предвидеть вновь разработанные менеджеры и используют менеджер с заранее установленным названием objects.

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

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

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

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

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

Бэкэнд

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

Нам необходимо разработать свой бэкэнд аутентификации, в директории приложения создаем файл backends.py:

from django.contrib.auth.backends import ModelBackend

from .models import User


class EmailBackend(ModelBackend):
    def authenticate(self, email=None, password=None, **kwargs):
        if email and password:
            try:
                user = User.active.get(email=email)
            except User.DoesNotExist:
                return None
            if user.check_password(password):
                return user

    def get_user(self, pk):
        try:
            return User.active.get(pk=pk)
        except User.DoesNotExist:
            return None

Бэкэнд имеет два обязательных метода: authenticate и get_user.

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

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

Настройки

Для внесения корректив открываем settings.py и добавляем приложение accounts:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'core.apps.CoreConfig',
    'accounts.apps.AccountsConfig',
]

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

AUTH_USER_MODEL = 'accounts.User'

ACCOUNTS_EMAILBACKEND = 'accounts.backends.EmailBackend'

AUTHENTICATION_BACKENDS = [
    'django.contrib.auth.backends.ModelBackend',
    ACCOUNTS_EMAILBACKEND,
]

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

Миграции

После любых изменений моделей, включая создание новых, необходимо создать файлы миграций командой makemigrations:

python manage.py makemigrations

Затем используем команду migrate:

python manage.py migrate

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

Администрирование

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

Переходим в файл admin.py и приводим к следующему виду:

from django.contrib import admin
from django.contrib.auth.forms import UserChangeForm, UserCreationForm
from django.contrib.auth.admin import UserAdmin as _UserAdmin

from .models import User


class UserChangeAdminForm(UserChangeForm):
    class Meta(UserChangeForm.Meta):
        model = User


class UserCreationAdminForm(UserCreationForm):
    class Meta(UserCreationForm.Meta):
        model = User


@admin.register(User)
class UserAdmin(_UserAdmin):
    form = UserChangeAdminForm
    add_form = UserCreationAdminForm

Укажем читабельное название для нашего приложения, отредактировав файл apps.py:

from django.apps import AppConfig


class AccountsConfig(AppConfig):
    name = 'accounts'
    verbose_name = 'Аккаунты'

Используя команду createsuperuser создаем супер-пользователя:

python manage.py createsuperuser

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

Далее запускаем отладочный сервер командой runserver:

python manage.py runserver 8000

И переходим к разделу администратора по локальной ссылке: http://127.0.0.1:8000/admin/.