Запрос order_by в Django выполняется невероятно медленно в Python, но быстро в DB
У меня есть следующие модели:
class Shelf(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=200, db_index=True)
slug = models.SlugField(max_length=200, editable=False)
games = models.ManyToManyField(Game, blank=True, through='SortedShelfGames')
objects = ShelfManager()
description = models.TextField(blank=True, null=True)
class SortedShelfGames(models.Model):
game = models.ForeignKey(Game, on_delete=models.CASCADE)
shelf = models.ForeignKey(Shelf, on_delete=models.CASCADE)
date_added = models.DateTimeField()
order = models.IntegerField(blank=True, null=True)
releases = models.ManyToManyField(Release)
objects = SortedShelfGamesManager.as_manager()
class Game(models.Model):
name = models.CharField(max_length=300, db_index=True)
sort_name = models.CharField(max_length=300, db_index=True)
...
У меня есть представление, в котором я хочу получить все игры пользователя SortedShelfGames, отличающиеся по отношению Game. Затем я хочу иметь возможность сортировать этот список SortedShelfGames по нескольким различным полям. Поэтому сейчас я делаю следующее внутри SortedShelfGamesManager (который наследует от models.QuerySet), чтобы получить список:
games = self.filter(
pk__in=Subquery(
self.filter(shelf__user=user).distinct('game').order_by('game', 'date_added').values('pk') # The order_by statement in here is to get the earliest date_added field for display
)
)
Это работает так, как должно. Однако, когда я пытаюсь сделать order_by('game__sort_name'), запрос выполняется вечно в моем python. Когда я пытаюсь использовать его на сайте, он просто завершается. Если я беру сгенерированный SQL и просто запускаю его в своей базе данных, он возвращает все результаты за доли секунды. Я не могу понять, что я делаю не так. В таблице SortedShelfGames миллионы записей, если это имеет значение.
Думаю, для этого не нужно Subquery.
Вот что я сделал, чтобы решить эту проблему. Вместо использования подзапроса я создал список первичных ключей, оценив то, что я использовал в качестве подзапроса, а затем ввел это в свой запрос. Это выглядит следующим образом:
pks = list(self.filter(shelf__user=user).distinct('game').values_list('pk', flat=True))
games = self.filter(
pk__in=pks)
)
games = games.order_by('game__sort_name')
В итоге это оказалось довольно быстро. По сути, это то же самое, что и метод Subquery, но все, что происходило под капотом в python/Django, замедляло этот процесс.