Geoffrey D
Geoffrey D

Reputation: 588

Django - extend the User model with a Profile but also different user kind

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

Answers (2)

Geoffrey D
Geoffrey D

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

Johan
Johan

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

Related Questions