Vaibhav
Vaibhav

Reputation: 215

Check which model is the current user an instance of in Django

The Employee model in my Django project has a OneToOneField relationship with the built-in Django User model. I have then further inherited this Employee model into two different Manager and Associate models. Now, when a user logs in, I want to check if this user is a Manager or an Associate in my HTML template so that I can display different options, depending on whether they are a Manager or an Associate. What is the best way to that?

Models.py-

GENDER = [('male','Male'),('female','Female')]

class Employee(models.Model):
    user_def = models.OneToOneField(User,on_delete=models.CASCADE,null=True)
    name = models.CharField(max_length = 50)
    age = models.IntegerField()
    gender = models.CharField(max_length = 10,choices = GENDER)

    def __str__(self):
        return self.name

class Manager(Employee):
    dept = models.CharField(max_length = 50)

    def __str__(self):
        return super().__str__()

class Associate(Employee):
    reports_to = models.ForeignKey(Manager,on_delete=models.CASCADE)

    def __str__(self):
        return super().__str__()

Upvotes: 3

Views: 330

Answers (1)

Scratch'N'Purr
Scratch'N'Purr

Reputation: 10399

Ooo, using model inheritance eh? Django has three types of model inheritance, and based on your problem, it seems like what you need is Meta inheritance.

In this case, you want to make your Employee abstract and in the user_def field, define the related_name that provides a backwards relationship from the user to the subclassed models, which in your case would be the Manager or Associate models. You can't use any related_name however. You need to include the app label and class within the related name, e.g.: related_name="%(app_label)s_%(class)s". Here, the app_label and class would be app labels and classes of Manager and Associate so if you have your models defined in an app called hr, then to get the backwards relation from user (user is the user instance) to manager, you use user.hr_manager. Likewise if the user is associate, use user.hr_associate.

So, this backwards relation then essentially answers your question because since user has 1-to-1 relation to your Employee model, the user can only either be a manager or an associate. They are mutually exclusive. Therefore, if your user is a manager, then that user will return a Manager instance if you ask for user.hr_manager and None if you ask for user.hr_associate.

Now I notice you have allowed null=True on your OneToOneField, so it is possible that the user will return None for both instances of user.hr_manager and user.hr_associate. Therefore, just remember to do a thorough if/elif/else check on the user.

GENDER = [('male','Male'),('female','Female')]

class Employee(models.Model):
    user_def = models.OneToOneField(
        User,
        on_delete=models.CASCADE,
        null=True,
        related_name="%(app_label)s_%(class)s"
    )
    name = models.CharField(max_length=50)
    age = models.IntegerField()
    gender = models.CharField(
        max_length=10,
        choices = GENDER
    )

    class Meta:
        abstract = True

class Manager(Employee):
    dept = models.CharField(max_length=50)

    def __str__(self):
        return self.name

class Associate(Employee):
    reports_to = models.ForeignKey(
        Manager,
        on_delete=models.CASCADE
    )

    def __str__(self):
        return self.name

EDIT: I haven't tested the code, specifically the part about user.hr_manager/user.hr_associate. They may actually throw an error instead of None if there is no backwards relationship. In this case, you can simply catch and ignore the errors and move on to the next if/elif/else check.

Upvotes: 2

Related Questions