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.