2025, Dec 28 12:04

Как исправить ошибку username is required в dj-rest-auth с email-логином и django-allauth

Пошаговое решение ошибки username is required при входе по email в dj-rest-auth и django-allauth: фиксация версии 65.2.0, корректные настройки, советы.

Когда вы настраиваете dj-rest-auth и django-allauth для входа только по email на кастомной модели пользователя, можно столкнуться с навязчивыми предупреждениями и непонятным ответом “username is required” при попытке авторизации. Это руководство показывает, что именно это провоцирует, почему так происходит в недавних релизах и как аккуратно разблокировать конфигурацию без изменения модели данных или внешнего API.

Обзор проблемы

В Django‑проекте с кастомной моделью пользователя, где идентификатором служит email, и с аутентификацией из приложения Next.js через dj-rest-auth, бэкенд выводит предупреждения о деприкации из dj_rest_auth/registration/serializers, а при входе может сообщать, что требуется username, хотя поле username было намеренно удалено.

Код для воспроизведения: кастомный пользователь и настройки

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

# models.py
import uuid
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import AbstractUser, PermissionsMixin, UserManager
class AccountBaseManager(UserManager):
    def _spawn_user(self, full_name, email, password=None, **extra):
        if not email:
            raise ValueError('Users must have an email address')
        email = self.normalize_email(email=email)
        u = self.model(email=email, name=full_name, **extra)
        u.set_password(password)
        u.save(using=self.db)
        return u
    def create_user(self, full_name=None, email=None, password=None, **extra):
        extra.setdefault('is_staff', False)
        extra.setdefault('is_superuser', False)
        return self._spawn_user(full_name=full_name, email=email, password=password, **extra)
    def create_superuser(self, full_name=None, email=None, password=None, **extra):
        extra.setdefault('is_staff', True)
        extra.setdefault('is_superuser', True)
        return self._spawn_user(full_name=full_name, email=email, password=password, **extra)
class Member(AbstractUser, PermissionsMixin):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    first_name = None
    last_name = None
    username = None
    name = models.CharField(max_length=255)
    email = models.EmailField(unique=True)
    is_active = models.BooleanField(default=True)
    is_superuser = models.BooleanField(default=False)
    is_staff = models.BooleanField(default=False)
    avatar = models.ImageField(upload_to='avatars/', null=True, blank=True)
    date_joined = models.DateTimeField(default=timezone.now)
    last_login = models.DateTimeField(blank=True, null=True)
    USERNAME_FIELD = 'email'
    EMAIL_FIELD = 'email'
    REQUIRED_FIELDS = []
    objects = AccountBaseManager()
    def __str__(self):
        return self.email
# settings.py (важные фрагменты)
AUTH_USER_MODEL = 'Users_app.Member'
ACCOUNT_USER_MODEL_USERNAME_FIELD = None
ACCOUNT_LOGIN_METHODS = {'email'}
ACCOUNT_SIGNUP_FIELDS = ['email*', 'name*', 'password1*', 'password2*']
ACCOUNT_EMAIL_VERIFICATION = 'none'
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'rest_framework.authtoken',
    'allauth',
    'allauth.account',
    'allauth.socialaccount',
    'dj_rest_auth',
    'dj_rest_auth.registration',
]
AUTHENTICATION_BACKENDS = [
    'allauth.account.auth_backends.AuthenticationBackend',
    'django.contrib.auth.backends.ModelBackend',
]
REST_AUTH = {
    'USE_JWT': True,
    'JWT_AUTH_COOKIE': 'access_token',
    'JWT_AUTH_REFRESH_COOKIE': 'refresh_token',
}
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'dj_rest_auth.jwt_auth.JWTCookieAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ],
}

Что на самом деле происходит

Предупреждения идут из недавних релизов django-allauth. Там добавили деприкацию и проверку конфигурации вокруг полей логина/регистрации, а формулировки сообщений изменили. В частности, проверка сверяет согласованность между методами входа и полями регистрации, и её уровень серьезности был пересмотрен.

Выполняется проверка, что ACCOUNT_LOGIN_METHODS согласуется с ACCOUNT_SIGNUP_FIELDS. Уровень серьезности этой проверки понижен с “critical” до “warning”, поскольку бывают валидные случаи, когда метод входа не совпадает с тем, что доступно при регистрации. Эту проверку (account.W001) можно заглушить через Django’s SILENCED_SYSTEM_CHECKS.

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

Надежное решение

Зафиксируйте версию django-allauth на 65.2.0. Убедитесь, что это отражено в файле зависимостей, и пересоберите окружение, чтобы закрепление реально сработало. Если используете Docker, после правки зависимостей пересоберите образ, иначе даунгрейд может не примениться.

# requirements.txt (фрагмент)
django-allauth==65.2.0

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

# settings.py (важные фрагменты после даунгрейда)
ACCOUNT_USER_MODEL_USERNAME_FIELD = None
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_AUTHENTICATION_METHOD = 'email'
ACCOUNT_USERNAME_REQUIRED = False

Эта комбинация убирает требование указывать username и согласует конфигурацию с потоком входа только по почте. Если вы видели ответ “username is required” при логине, проверьте, что пакет действительно понижен и стек перезапущен. В контейнерной среде простого перезапуска недостаточно — требуется пересборка, чтобы подтянуть новую сборку. Также убедитесь, что старая версия пакета не осталась в окружении, проверив фактически разрешённые версии.

Почему это важно

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

Итоги и практические советы

Если связка dj-rest-auth и django-allauth выдаёт предупреждения о полях username или email и навязывает “username is required” в сценарии входа только по почте, закрепите django-allauth на версии 65.2.0 и переключитесь на email-ориентированные флаги, показанные выше. Подтвердите даунгрейд по lock/requirements и пересоберите рантайм, если используете контейнеры. Следите за релиз-нотами при обновлении библиотек аутентификации: фиксируйте версии заранее и меняйте настройки только после проверки поведения в контролируемой среде.