Внутреннее соединение двух кверисетов Django из связанных моделей
TL;DR: Существует ли способ внутреннего объединения двух связанных наборов запросов, если они основаны на разных моделях?
Предположим, я пытаюсь управлять контролем доступа для файловой системы, и у меня есть модели для пользователя, папки и файла. Пользователь может получить доступ к файлу, если он общедоступен, ИЛИ если он может получить доступ к папке файла. У меня уже есть Django queryset, который включает все папки для пользователя:
# I already have this queryset...
user_folders = Folder.objects.filter(
Q(is_public=True) | Q(owner=user) | Q(folder_shares__user=user),
deleted_at__isnull=True,
)
Теперь проблема: я также хочу получить набор условий, который включает все файлы для пользователя. И я хочу переработать набор запросов users_folders, чтобы мне не пришлось снова переписывать все те же условия.
# ...so I don't want to rewrite the same conditions for this queryset
user_files = File.objects.filter(
Q(is_public=True)
| Q(folder__is_public=True)
| Q(folder__owner=user)
| Q(folder__folder_shares__user=user),
deleted_at__isnull=True,
folder__deleted_at__isnull=True,
)
Это упрощенный пример. В моей реальной кодовой базе два набора запросов намного сложнее, и это трехуровневая иерархия. Поэтому в настоящее время, если правила совместного доступа меняются, мне приходится помнить об обновлении 3 различных сложных запросов во всем приложении. Это легко испортить, а затраты на ошибку очень высоки. Кажется, что должен быть лучший способ.
В качестве посредственного решения я рассматривал возможность сделать что-то вроде этого:
# This is inefficient if `user_folders` contains many results
user_files = File.objects.filter(
Q(is_public=True) | Q(folder__in=user_folders),
deleted_at__isnull=True,
)
Но на самом деле это выполняет два запроса, и первый запрос забирает в память все из user_folders. В моем приложении есть много тысяч общих папок, поэтому делать это для каждого запроса кажется до боли неэффективным.
Это просто внутреннее соединение между двумя различными наборами запросов, поэтому я удивлен, что не смог найти никакого практичного/эффективного способа сделать это. Я что-то упускаю?