Django: установить значение по умолчанию для нового поля модели, которое имеет unique=True

Существует пользовательская модель пользователя, которая наследуется AbstractUser в Django.

У модели есть username = None, ниже приведена модель:

class User(AbstractUser):
    username = None
    email = models.EmailField(_("Email address"), unique=True)

    USERNAME_FIELD = "email"

Я хочу удалить username = None, чтобы мы могли сохранять и имена пользователей.

Но проблема в том, что у нас есть различные пользователи в базе данных.

и когда я удаляю username = None и пытаюсь мигрировать, я получаю запрос:

Невозможно добавить не нулевое поле 'username' в user без указания значения по умолчанию. Это происходит потому, что базе данных нужно чем-то заполнить существующие строки. Пожалуйста, выберите исправление:

  1. Предоставить одноразовое значение по умолчанию сейчас (будет установлено для всех существующих строк с нулевым значением для этого столбца)
  2. .
  3. Выйти и вручную определить значение по умолчанию в models.py.
  4. .

Я не хочу переопределять поле username класса AbstractUser.

AbstractUser > username:

username_validator = UnicodeUsernameValidator()

    username = models.CharField(
        _("username"),
        max_length=150,
        unique=True,
        help_text=_(
            "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only."
        ),
        validators=[username_validator],
        error_messages={
            "unique": _("A user with that username already exists."),
        },
    )

Я хочу, чтобы username принимал значение email, если не указано другое значение.

Как я могу предоставить значение по умолчанию?

Вы можете использовать миграцию для старых значений и написать сеттер для имени пользователя в вашем Serializer для новых записей

Вы можете использовать pre_save сингал следующим образом:

# signals.py

from django.db.models.signals import pre_save
from django.dispatch import receiver

@receiver(pre_save, sender=User)
def fill_username(sender, instance, *args, **kwargs):
    if not instance.username:
        instance.username = instance.email

# apps.py

class UserConfig(AppConfig):
    ...

    def ready(self):
        import users.signals

Таким образом, он всегда будет проверять, введено ли имя пользователя. В случае, если оно пустое, оно будет копировать значение из email.

Для существующих пользователей вам может потребоваться вызвать save() вручную или с помощью цикла for в shell:

$ python manage.py shell

>> from users.models import User
>> for user in User.objects.all():
...    user.username = user.email
...    user.save()

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

import django.contrib.auth.models
from django.db import migrations, models
import users.models


def forwards_func(apps, schema_editor):
    User = apps.get_model("users", "User")
    User.objects.update(username=models.F("email"))


def reverse_func(apps, schema_editor):
    pass


class Migration(migrations.Migration):

    dependencies = [
        ('users', '0001_initial'),
    ]

    operations = [
        migrations.AlterModelManagers(
            name='user',
            managers=[
                ('manager', users.models.UserManager()),
                ('objects', django.contrib.auth.models.UserManager()),
            ],
        ),
        migrations.AddField(
            model_name='user',
            name='username',
            field=models.EmailField(max_length=254, unique=True, null=True),
            preserve_default=False,
        ),
        migrations.RunPython(forwards_func, reverse_func),
        migrations.AlterField(
            model_name='user',
            name='username',
            field=models.EmailField(max_length=254, unique=True),
        ),
    ]

Вот объяснение изменений:

  • Сначала сделайте столбец нулевым, добавив null=True к новому полю username.
  • Обновите колонку имени пользователя существующих пользователей с помощью действия RunPython. В forward_func мы используем значение колонки email для username. Обратная функция может быть пустой, так как колонка будет удалена.
  • Теперь вы можете сделать поле обязательным, установив null в его значение по умолчанию, False, через AlterField.
Вернуться на верх