Django запрос, аннотирование цепочки связанных моделей
У меня есть следующая схема с PostgreSQL.
class Video(models.Model):
title = models.CharField(max_length=255)
created_at = models.DateTimeField()
disabled = models.BooleanField(default=False)
view_count = DecimalField(max_digits=10, decimal_places=0)
class TopVideo(models.Model):
videos = (Video, on_delete=models.CASCADE, primary_key=True)
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
video = models.ForeignKey(Video, related_name="comments", on_delete=models.CASCADE)
Причина, по которой у меня есть модель TopVideo, заключается в том, что у меня миллионы видео, и запрос к ним занимает много времени на дешевом сервере, поэтому у меня есть вторичная модель, которая заполняется задачей celery, промывается и заново заполняется при каждом запуске, что делает время загрузки домашней страницы намного быстрее. Задача выполняет запросы, которые вы видите далее, и сохраняет их в модели TopVideo. Таким образом, задача может выполняться долго, но пользователю больше не нужно ждать дорогостоящего запроса.
До использования модели TopVideo я выполнял такой запрос для своей домашней страницы:
videos = (
Video.objects.filter(created_at__range=[start, end])
.annotate(comment_count=Count("comments"))
.exclude(disabled=True)
.order_by("-view_count")[:100]
)
Это прекрасно работало, и у меня был доступ к "comment_count" в моем шаблоне, где я мог легко показать количество комментариев для каждого видео.
Но теперь, когда я делаю этот запрос:
top_videos = (
TopVideo.objects.all().annotate(comment_count=Count("video__comments"))
.select_related("video")
.order_by("-video__view_count")[:100]
)
и с помощью простого цикла for,
videos = []
for video in top_videos:
videos.append(video.video)
Я отправляю видео в шаблон для рендеринга. Моя проблема в том, что у меня больше нет доступа к "comment_count" внутри шаблона, и естественно поэтому; я больше не отправляю кверисет. Как я могу получить доступ к comment_count?
То, что я пробовал:
- Sending the TopVideo query to template did not work. They're a bunch of TopVideo objects, not Video objects.
- I added this piece of code in my template "{{ video.comments.count }}" but this makes 100 requests to the database, which is not really optimal.
Вы можете установить .comment_count
на ваши Video
объекты с помощью:
videos = []
for top_video in top_videos:
video = top_video.video
video.comment_count = top_video
videos.append(video)
но, несмотря на это, мне неясно почему вы запрашиваете TopVideo
, если вы практически вычеркнули TopVideo
контекст из видео.
Если вы хотите получить Video
s, для которых существует объект TopVideo
, вы можете работать с:
videos = Video.objects.filter(
created_at__range=[start, end], topvideo__isnull=False
).annotate(
comment_count=Count('comments')
).exclude(disabled=True).order_by('-view_count')[:100]
Таким образом, topvideo__isnull=False
будет отфильтровывать Video
, которые не являются TopVideo
s.