Как получить сумму обратного отношения для каждого набора запросов для 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 %}
Какой способ наиболее подходящий для этого?