'Queryset for mutiple table joins

class Organization(models.Model):
    """Organization Model"""
    name = models.CharField(max_length=100, blank=False)
    is_active = models.BooleanField(default=True)

class Department(models.Model):
    """Department Model"""
    org = models.ForeignKey(Organization, on_delete=models.PROTECT, related_name='Department_Org')
    name = models.CharField(max_length=100, blank=False)
    is_active = models.BooleanField(default=True)

class Project(models.Model):
    """Project Model"""
    dept = models.ForeignKey(Department, on_delete=models.PROTECT, related_name='Project_Dept', null=True)
    no = models.CharField(max_length=100, blank=False)
    name = models.CharField(max_length=255, blank=True)
    is_active = models.BooleanField(default=True)

class Subscription(models.Model):
    """Subscription Model"""
    MODULE_CHOICES = (
        ('ECM', 'Change Management'),
        ('DMS', 'Document Management'),
        ('AIM', 'Deviation Management'),
        ('PMS', 'Project Management'),
        ('ADM', 'Admin'),
        ('RPT', 'Reports'),
        ('MTS', 'My Tasks'),
    )
    project = models.ForeignKey(Project, on_delete=models.PROTECT, related_name='Subscription_Project')
    module = models.CharField(max_length=3, choices=MODULE_CHOICES)

class User(AbstractBaseUser, PermissionsMixin):
    """Custom User Model"""
    email = models.EmailField(max_length=255, unique=True)
    name = models.CharField(max_length=255)
    displayname = models.CharField(max_length=255, blank=True)
    fname = models.CharField(max_length=255, blank=True)
    lname = models.CharField(max_length=255, blank=True)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)

class UserProjectSubscriptionRole(models.Model):
    """User Role Model"""
    user = models.ForeignKey(User, on_delete=models.PROTECT, related_name='UserProjectRole_user')
    project = models.ForeignKey(Subscription, on_delete=models.PROTECT, related_name='UserProjectRole_Module')
    role = models.ForeignKey(RoleType, on_delete=models.PROTECT, related_name="UserProjectRole_role")

I need to write queryset to get Organizations to which User is associated.



Solution 1:[1]

You can filter with:

Organization.objects.filter(
    Department_Org__Project_Dept__UserProjectRole_Module__user=my_user
)

However the related_name=… values [Django-doc] are cumbersome: these are names to access the related objects that point to an object. Your UserProjectSubscriptionRole is also a junction model of a ManyToManyField. You can rewrite the modeling to:

class Department(models.Model):
    org = models.ForeignKey(Organization, on_delete=models.PROTECT, related_name='departments')
    # …

class Project(models.Model):
    dept = models.ForeignKey(Department, on_delete=models.PROTECT, related_name='projects', null=True)
    # …

class Subscription(models.Model):
    project = models.ForeignKey(Project, on_delete=models.PROTECT, related_name='subscriptions')
    # …

class User(AbstractBaseUser, PermissionsMixin):
    # …
    projects = models.ManyToManyField(
        Project,
        trough='UserProjectSubscriptionRole',
        related_name='users'
    )

class UserProjectSubscriptionRole(models.Model):
    user = models.ForeignKey(User, on_delete=models.PROTECT, related_name='userprojectroles')
    project = models.ForeignKey(Subscription, on_delete=models.PROTECT, related_name='userprojectroles')
    role = models.ForeignKey(RoleType, on_delete=models.PROTECT, related_name='userprojectroles')

Then you can filter with:

Organization.objects.filter(
    departments__projects__users=my_user
)

This seems more readable and compact.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Willem Van Onsem