Reputation: 588
I want to extend the user model in Django (2.2) and combine it with a Host and a Guest entities that have also specific fields.
In the official documentation, it is recommended to create a "Profile" class with a OneToOne field that reference the User primary key.
I can see 3 ways of doing it:
Solution 1: Profile, Host and Guest model:
class Profile(models.Model):
k_user = models.OneToOneField(User, on_delete=models.CASCADE)
language = models.CharField(max_length=2)
class Host(models.Model):
k_user = models.OneToOneField(User, on_delete=models.CASCADE)
host_field= models.CharField(max_length=500)
class Guest(models.Model):
k_user = models.OneToOneField(User, on_delete=models.CASCADE)
guest_field = models.BooleanField(null=False)
Solution 2: Host and Guest model (with Profile fields duplicated)
class Host(models.Model):
k_user = models.OneToOneField(User, on_delete=models.CASCADE)
language = models.CharField(max_length=2)
host_field = models.CharField(max_length=500)
class Guest(models.Model):
k_user = models.OneToOneField(User, on_delete=models.CASCADE)
language = models.CharField(max_length=2)
guest_field = models.BooleanField(null=False)
Solution 3: Profile model (containing Guest and Host fields)
class Profile(models.Model):
k_user = models.OneToOneField(User, on_delete=models.CASCADE)
language = models.CharField(max_length=2)
is_host = models.BooleanField(null=False)
guest_field = models.BooleanField(null=False)
host_field = models.CharField(max_length=500)
All those solutions are working.
My question is: "Which one is the smartest, all things considered" (less database access, less code to write, easier to maintain, less limitations, etc..)
Upvotes: 1
Views: 4328
Reputation: 588
After digging further into Django's doc and reading the article mentioned by @sam that explain how to implement multi user types in Django, I found my answer.
It is written in the Django doc that "it’s highly recommended to set up a custom user model, even if the default User model is sufficient for you".
Here is what it gives in my case:
class User(AbstractUser):
is_guest = models.BooleanField(default=False)
is_host = models.BooleanField(default=False)
language = models.CharField(max_length=2)
class Host(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
host_field = models.CharField(max_length=500)
class Guest(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
guest_field = models.BooleanField(null=False)
in settings.py:
AUTH_USER_MODEL = 'path.to.User'
Guest or Host record are inserted when you create a new user:
user = User.objects.create_user(...)
if is_host:
Host.objects.create(user=user)
else:
Guest.objects.create(user=user)
I appreciate the fact that I can detect the user "type" in the request object (with request.user.is_host
).
By extending the user class, you can also use the email
field for login, and make it unique:
class User(AbstractUser):
[...]
email = models.EmailField(unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
If once in production you chose to add fields in the User model, it is possible if you have set up a custom user model.
Otherwise you will be stuck with profiles, so I advise you to follow Django's guidelines and always extending the user class, even if you don't need it (yet).
Upvotes: 4
Reputation: 3611
I would propose a 4:th way, using a mixin with an abstract model. This will derive the fields of the abstract model to the ones that you apply it to. This way you don't need to rewrite code and still apply it to different models:
class ProfileMixin(models.Model):
k_user = models.OneToOneField(User, on_delete=models.CASCADE)
language = models.CharField(max_length=2)
class Meta:
abstract = True
class Host(ProfileMixin):
host_field = models.CharField(max_length=500)
class Guest(ProfileMixin):
guest_field = models.BooleanField(null=False)
Upvotes: 0