Django: избегайте многократных запросов к БД для рекурсивной модели

У меня есть следующие модели:

class Topic(models.Model):
    ...

class Article(models.Model):
    ...

class ArticleInTopic(models.Model):
    topic = models.ForeignKey(Topic, on_delete=models.PROTECT)
    article = models.ForeignKey(Article, on_delete=models.PROTECT)
    depends_on = models.ForeignKey(ArticleInTopic, on_delete=models.PROTECT)

    class Meta:
        unique_together = ('topic', 'article', 'depends_on')

С помощью этого набора моделей я пытаюсь выразить следующую ситуацию: есть несколько изучаемых тем, каждая из которых состоит из нескольких статей. Однако для изучения темы необходимо прочитать статьи, относящиеся к ней, но не в любом порядке, а в порядке, определяемом зависимостями статей. Это означает, что в контексте темы у статьи может быть другая статья, которую необходимо прочитать до прочтения этой статьи. Гарантируется, что статья, от которой зависит текущая статья, из той же темы.

Итак, в основном, вся эта структура выглядит как ациклический (это гарантировано) граф с отношением узлов родитель-ребенок. В бизнес-логике моего приложения я собираюсь отсортировать граф топологически, чтобы я мог сказать пользователю, какую статью читать первой, второй и т.д.

Однако проблема заключается в том, как Django извлекает данные ForeignKey из базы данных. AFAIK он использует N+1 запрос для получения всех внешних ключей. Есть решение - использовать select_related, но это работает только тогда, когда вы можете указать точные поля, которые должны быть запрошены в том же запросе, что и основная информация. В моем случае это невозможно, так как я не знаю заранее, сколько дочерних узлов имеет каждый узел, поэтому я не могу перечислить их все в select_related.

Вместо этого я думал о том, чтобы получить все ArticleInTopic объекты, которые имеют одинаковый topic внешний ключ (название темы приходит от пользователя, поэтому я знаю его к тому времени, когда мне нужно будет показать ему статьи) и затем топологически отсортировать их в памяти. Но я не уверен, поймет ли Django, что он уже собрал все нужные объекты, когда я обращусь к одному из объектов depends_on field.

Например, я получаю 2 ArticleInTopic объекта для темы "Автомобили", допустим, эти объекты - A и B. B зависит от A. Django уже запросил их, и они находятся в памяти. Теперь, что произойдет, если я сделаю B.depends_on? Сделает ли Django еще один запрос к БД, чтобы выбрать B? Или он достаточно умен, чтобы понять, что B уже был извлечен предыдущим запросом? Если это не так, есть ли способ предотвратить дополнительные запросы к БД?

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