Как получить все многие объекты в наборе объектов запроса

У меня есть модель Department, в которой есть Role, с которыми связаны User. Я знаю пользователя, но мне нужно получить user.role.department_set.all() (все отделы, к которым принадлежит роль пользователя), а затем получить все role в каждом из этих отделов.

Модели:

class User(AbtractUser):
    role = models.ForeignKey(Role)

class Role(models.Model):
    name = models.CharField()

class Department(models.Model):
    name = models.CharField()
    roles = models.ManyToManyField()

Как я могу получить все отделы, которые содержат роль пользователя, а затем получить роли всех этих отделов?

В настоящее время я делаю нечто подобное, но это очень дорогой поиск:

    def get_departments(self):
        """Returns a list of departments the user is in."""
        return self.role.department_set.all()

    def get_users_departments_roles(self):
        """Returns all the roles of all the departments the user is in."""
        departments = self.get_departments()
        roles = Role.objects.none()
        for department in departments.iterator():
            for role in department.roles.all():
                roles |= Role.objects.filter(name=role.name)
        return roles

Попутный вопрос: После того, как я все это напечатал, будет ли лучше просто добавить поле department к каждому пользователю? Меня беспокоит то, что если роль изменится, мне придется одновременно изменить и отдел, и это добавит сложности.

Вы можете отфильтровать набор запросов Role и проследить отношения ManyToManyField в обратном направлении, используя имя department

Вы можете передать набор запросов в фильтр __in для создания вложенного запроса

    def get_users_departments_roles(self):
        """Returns all the roles of all the departments the user is in."""
        departments = self.get_departments()
        return Role.objects.filter(department__in=departments).distinct()

Обратите внимание, что некоторые базы данных (MySQL) не очень хорошо выполняют вложенные запросы, смотрите это предупреждение в конце этого раздела в документации

Вернуться на верх