Django can't change username in custom User model
I have the following User model in Django's models.py:
class User(AbstractBaseUser):
username = models.CharField(max_length=30, unique=True, primary_key=True)
full_name = models.CharField(max_length=65, null=True, blank=True)
email = models.EmailField(
max_length=255, unique=True, validators=[EmailValidator()]
)
When trying to update a username via shell python manage.py shell:
userobj = User.objects.get(username="username1")
userobj.username = user.lower()
userobj.save()
It seems that it is trying to create a new user, for which the UNIQUE constraint of the e-mail is violated:
django.db.utils.IntegrityError: UNIQUE constraint failed: data_app_user.email
Any solutions? Thank you!
It happens because username field is the primary key in your model. Changing object's primary key and calling save will create a new object.
In this case it's better to use another field as primary key:
class User(AbstractBaseUser):
id = models.IntegerField(primary_key=True)
username = models.CharField(max_length=30, unique=True)
... # other fields
Also, If you want update some fields on an instance it's better to use the update method of the QuerySet class.
>>> User.objects.filter(username=old_username).update(name=new_username)
This is one of the reasons not to use a username as primary key. Indeed, Django uses the primary key as token to check if two items are the same. If the primary key changes, and you save it, it will first try to update the user with that primary key, and if that fails, make an insert.
This can thus produce several unwanted scenarios: one is that it duplicates data, in case the new primary key does not exist, but perhaps even worse: it can result in copying the data of the user to a record of the "new" username, and thus essentially overriding data of that user.
As a general rule of thumb, it is better to consider primary keys "blackbox values". Yes, an AutoField works with an integer, but it is better to assume that it is a token where we don't know the details. The fact that it is an integer,
In most database courses, one learns that the primary key is the (subset of) column(s) that makes a record unique, and from a database perspective, that is the right approach, but it is a "Djangoism" that the primary keys use a uniform type, and often don't store real information. This is also useful if you would later work with a GenericForeignKey [Django-doc], so that means there are essentially two options: using an AutoField, or a UUIDField, but regardless, it is better not to store information in the primary key.
If you really want to insist, you can update the primary key with:
User.objects.filter(pk='username1').update(pk='username2')
Which will make a query that looks like:
UPDATE app_name_user
SET username = 'username2'
WHERE username = 'username1'
But that is probably not a good idea either, since now you omit validation, signals, and all other "mechanics" that Django has put in place.
So I would strongly advise to rewrite the model and don't make username the primary key. You can still set it unique=True [Django-doc].