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 без указания значения по умолчанию. Это происходит потому, что базе данных нужно чем-то заполнить существующие строки. Пожалуйста, выберите исправление:
- Предоставить одноразовое значение по умолчанию сейчас (будет установлено для всех существующих строк с нулевым значением для этого столбца) .
- Выйти и вручную определить значение по умолчанию в models.py. .
Я не хочу переопределять поле 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
.