Reputation: 910
There is a custom user model which inherits AbstractUser
in Django.
The model has username = None
, below is the model:
class User(AbstractUser):
username = None
email = models.EmailField(_("Email address"), unique=True)
USERNAME_FIELD = "email"
I want to remove username = None
so that we can save usernames as well.
But the issues is we have various users in the database.
and when I remove the username = None
and try to migrate, I get the prompt:
It is impossible to add a non-nullable field 'username' to user without specifying a default. This is because the database needs something to populate existing rows. Please select a fix:
I don't want to override the username
field of AbstractUser
class.
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."),
},
)
I want username
to take value of email
if a different value is not provided.
How can I provide the default value?
Upvotes: 1
Views: 1700
Reputation: 6378
You can use pre_save
singal like that:
# 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
In this way it will always check if username is entered. In case it's empty it will copy value from email.
For existing Users you might need to call save() manually or with for loop in shell:
$ python manage.py shell
>> from users.models import User
>> for user in User.objects.all():
... user.username = user.email
... user.save()
Upvotes: 1
Reputation: 2146
You can use a custom migration. When creating your migration, provide a default value, then edit the generated migration with something like this:
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),
),
]
Here's the explanation of the changes:
null=True
to the new username
field.RunPython
action. In the forward_func
, we use the value of email
column for username
. Reverse function can be empty because the column will be removed.null
to its default value, False
, via AlterField
.Upvotes: 3
Reputation: 9
You can use migration for older values and write the setter for username in your Serializer for the new entries
Upvotes: 0