Django_filters рекурсивная фильтрация

Итак, у меня есть фильтр django, который выглядит следующим образом:

class ModelFilter(FilterSet):
    user_name = CharFilter(method="some_method")
    boss = ModelChoiceFilter(...)

Моя модель выглядит примерно так:

class Employee(Model):
username = Charfield(...)
boss = ForeignKey("self", ''')

Таким образом, сотрудник может быть начальником другого сотрудника. Теперь этот фильтр будет возвращать правильный набор запросов в зависимости от того, какие значения ищет пользователь. Допустим, у нас есть три объекта:

O1= Employee(usename="u1", boss=None)
O2= Employee(usename="u2", boss=O1)
O3= Employee(usename="u3", boss=O2)

Если я применю вышеупомянутый фильтр к этим данным и выполню поиск по boss=O1, то в результате получу объект O2. Я хочу добавить в фильтр новое булево поле, скажем, "with_subordinates", которое будет возвращать все "древовидные" отношения, если оно истинно. Так, например, если я буду искать: boss=O1, with_subordinates=True, результатом будут O2 и O3. В принципе, с этой новой опцией фильтр должен рекурсивно показывать сотрудников, предыдущих сотрудников и т.д.

Есть ли способ достичь чего-то подобного?

Кроме основных концепций, мы будем делать это питоническим способом. Для этого нам нужно получить все объекты и их boss_id из базы данных.

boss_object_ids = [1,] # this is the input boss id
have_to_wait = True

while have_to_wait:
    prev_ids = boss_object_ids
    if Employee.objects.filter(boss__in=boss_object_ids).count() > 0:
    
        boss_ids_list = Employee.objects.filter(boss__in=boss_object_ids).values_list('id', flat=True)
        boss_object_ids.extend(boss_ids_list)
        boss_object_ids = list(set(boss_object_ids))
        if set(boss_object_ids) == set(prev_ids):
            have_to_wait = False
all_subordinates = Employee.objects.filter(boss__in=boss_object_ids)

        

Объяснение:

  • В первый раз мы передаем на вход boss_id = [1,] или какое-то целое число.
  • Второй раз он зациклится и найдет 2,3,4,5 идентификаторы, следовательно, список будет [1,2,3,4,5].
  • В третий раз он получит идентификаторы 2,3,4,1,6, следовательно, список будет [1,2,3,4,5,6].
  • Подобным образом он будет фильтровать до тех пор, пока снова не получит тот же список.
  • То есть, в данном примере, он будет проверять, нет ли лишнего числа, кроме [1,2,3,4,5,6].
  • Наконец, мы можем фильтровать с помощью списка boss_object_ids. Это желаемый список всех подчиненных id.
Вернуться на верх