Reputation: 16107
In django.contrib.auth.models.User
, both first_name
and last_name
fields have blank=True
. How can I make them blank=False, null=False
in my own model?
Here is my implementation, which basically follows the instructions of Extending the existing User model:
models.py
class Fellow(models.Model):
user = models.OneToOneField(
User,
on_delete=models.CASCADE
)
first_name = models.CharField(
_("first_name"),
max_length=30,
)
last_name = models.CharField(
_("last_name"),
max_length=30,
)
# other fields omitted
from . import signals
signals.py
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import Fellow
@receiver(post_save, sender=User)
def create_fellow_on_user_create(sender, instance, created, **kwargs):
if created:
Fellow.objects.create(user=instance)
However, I got an error when testing in python manage.py shell
:
>>> f = Fellow.objects.create(username='username', password='passwd')
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/Users/sunqingyao/Envs/django_tutorial/lib/python3.6/site-packages/django/db/models/manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/Users/sunqingyao/Envs/django_tutorial/lib/python3.6/site-packages/django/db/models/query.py", line 392, in create
obj = self.model(**kwargs)
File "/Users/sunqingyao/Envs/django_tutorial/lib/python3.6/site-packages/django/db/models/base.py", line 571, in __init__
raise TypeError("'%s' is an invalid keyword argument for this function" % list(kwargs)[0])
TypeError: 'username' is an invalid keyword argument for this function
Upvotes: 5
Views: 3396
Reputation: 53734
You have misunderstood the tutorial. The employee example you are referring to explains how to add to the User
this is done by way of a OneToOneField
that leaves the original User
model intact but for all practical purposes it's as if the User
had a new field called department
. This is implemented by creating two separate tables in the database.
There is one caveat, in the Employee
and the Fellow
you can't directly create new instances in the way you have tried without first creating a User
instance. That's because neither of those models directly own a username field. So do it in two steps
user = User(username='Sun')
user.set_password('123')
user.save()
f = Fellow(user=user)
But this approach is still less than desirable. you have a first_name and last_name field in both models. It will always lead to confusion. The proper method is to subclass AbstractBaseUser
class Fellow(AbstractBaseUser):
first_name = models.CharField(max_length=254)
AbstractBaseUser
is a bare bones User (see the code: https://github.com/django/django/blob/master/django/contrib/auth/base_user.py#L47) it does not have a first_name and last_name so you can add them with the behaviour that you want.
Upvotes: 2
Reputation: 3705
The very first line of documentation you are referring to says
There are two ways to extend the default User model without substituting your own model
The keyword is without substituting
I see 3 options here:
1) Derive you User class from AbstractBaseUser and define all required fields. You can take AbstractUser as an example.
2) Add validation either to UserSerializer or to pre_save
signal on user save model and validate first_name
and last_name
fields. The simplest way, but not a good option for a long term project.
3) Do the way you've chosen by adding a reference to a User
model as a user
. Define first_name
and last_name
but ensure than there is no duplication of those fields between Fellow.first_name
and Fellow.user.first_name
. This could be hard as other developers may misunderstood the idea.
An error you get happens because User model has not been created and the right way to initiate a Fellow
instance would be:
user = User.objects.create(username='test', email='[email protected]')
fellow = Fellow.objects.create(user=user, first_name='firstname', last_name='last_name')
Upvotes: 1