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:
- Provide a one-off default now (will be set on all existing rows with a null value for this column)
- Quit and manually define a default value in models.py.
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?
CodePudding user response:
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:
- First, make the column nullable by adding
null=True
to the newusername
field. - Update the username column of existing users via
RunPython
action. In theforward_func
, we use the value ofemail
column forusername
. Reverse function can be empty because the column will be removed. - Now you can make the field required by setting
null
to its default value,False
, viaAlterField
.
CodePudding user response:
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()
CodePudding user response:
You can use migration for older values and write the setter for username in your Serializer for the new entries