Как обратиться ко всем дочерним объектам с помощью существующих запросов в самоссылающемся внешнем ключе?
У меня есть модель, которая представляет владельца. В этой модели есть внешний ключ к самому себе, чтобы представить родительскую сущность. Есть еще одна модель под названием актив с внешним ключом на владельца. Цель внешнего ключа родителя - эмулировать корпоративную структуру, такую, что родитель "владеет" активом, внешним ключом которого является он сам или дочерняя компания:
Class Owner(models.Model):
parent = models.ForeignKey(
“self”,
)
Class Asset(models.Model):
owner = models.ForeignKey(
Owner,
)
Есть ли способ вернуть все активы, принадлежащие материнской компании и всем ее дочерним компаниям, просто сославшись на родительскую компанию (например, Asset.object.filter(owner=parent))? Я знаю, что могу создать метод для возврата кверисета всех дочерних компаний родителя, а затем отфильтровать все активы в этом кверисете по владельцу, но я надеюсь избежать большого рефакторинга, учитывая, что в существующей кодовой базе нет понятия владельца родителя.
Моя первая мысль - пользовательский менеджер, но я не думаю, что это изменит поведение существующих запросов, которые все работают с менеджером по умолчанию. Могу ли я перегрузить фильтр на этой модели? Если мне нужно переосмыслить дизайн, это хорошо, но я думаю, что этот подход чище и передает поведение, которое мы хотим. Спасибо!
Возможно, это неудовлетворительный или неполный ответ, но у меня есть необработанный запрос postgres, который возвращает всех детей для родителя в одном запросе. В нем используется рекурсивный запрос
class Owner(models.Model):
parent = models.ForeignKey('self', on_delete=models.SET_NULL, null=True, blank=True)
def get_all_children(self):
return Owner.objects.raw(f"""
with recursive t(id, parent_id) as
(
select id, parent_id
from app_owner where id={self.id}
union all
select b.id, b.parent_id
from app_owner b
join t on b.parent_id=t.id
)
select * from t
""")
Это можно использовать и для фильтрации активов
Asset.objects.filter(owner__in=Owner.objects.get(id=1).get_all_children())