Search code examples
pythondjangodjango-modelsdjango-users

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..)


Solution

  • 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).