Как получить сумму обратного отношения для каждого набора запросов для ListView?

Сколько голосов набрала история? Что я должен сделать, чтобы увидеть это в ListView? И как я могу сделать это, не застряв на проблеме n+1?

models.py

class Story(models.Model):
    author = models.ForeignKey(
        MyUser,
        on_delete=models.CASCADE,
        related_name='story'
    )
    title = models.CharField(max_length=255)
    slug = models.SlugField(max_length=255, unique=True)
    content = models.TextField(max_length=2500, default='')
    draft = models.BooleanField(default=True)
    completed = models.BooleanField(default=False)
    expiration = models.DateTimeField(default=timezone.now()+timezone.timedelta(days=1))
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['-created_at']
        indexes = [models.Index(fields=['draft', 'completed']),]

    def __str__(self):
        return self.title

    def save(self, *args, **kwargs):
        if not self.id:
            self.slug = slugify(self.title)
            
            for slug_id in itertools.count(1):
                if not Story.objects.filter(slug=self.slug).exists():
                    break
                self.slug = f'{self.slug}-{slug_id}'

        return super(Story, self).save(*args, **kwargs)

    def get_absoulte_url(self):
        return reverse('story:detail-story', kwargs={'slug': self.slug})

class Rating(models.Model):
    story = models.ForeignKey(
        Story,
        on_delete=models.CASCADE,
        related_name='rating'
    )
    user = models.ForeignKey(
        MyUser,
        on_delete=models.CASCADE,
        related_name='rating'
    )
    rate = models.SmallIntegerField(validators=[MinValueValidator(-1), MaxValueValidator(1)], default=0)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=['story', 'user'], name='rating validation'
            )
        ]

    def __str__(self):
        return f'{self.created_at}'

views.py

class IndexListView(ListView):
    model = Story
    context_object_name = 'stories'
    template_name = 'story/index.html'
    paginate_by = 10
    queryset = Story.objects.select_related('author').filter(completed=False, draft=False)

Я хочу это: Количество голосов будет отображаться рядом с каждой историей на странице листинга.

Я сделал это, используя тег шаблона. Однако, когда я проверяю с помощью django-debug-toolbar, я сталкиваюсь с проблемой n+1. На самом деле я не очень хорошо разбираюсь в Generic Views. Я думаю, что это можно сделать без использования тега шаблона. Я не прав?

template_tag

@register.filter
def story_rate(value):
    rate = Rating.objects.filter(story__slug=value).aggregate(total_rate=Coalesce(Sum('rate'), 0))
    return rate['total_rate']

шаблон

            {% for story in stories %}
            <div class="card">
                <div class="card-body">
                  <h5 class="card-title"><a href="{% url 'story:detail-story' story.slug %}" class="text-dark" style="text-decoration: none;">{{ story.title }}</a></h5>
                  <p class="card-text"><a href="{% url 'story:detail-story' story.slug %}" class="text-dark" style="text-decoration: none;">{{ story.content | truncatewords:20 }}</a></p>
                  <span class="text-secondary"> {{ story.slug | story_rate |join:" - " }}</span>
                  <span class="text-secondary">| author: {{ story.author }}</span>
                </div>
            </div>
            <br>
            {% endfor %}

Какой способ наиболее подходящий для этого?

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