django - 减少 Django 播客应用程序的数据库点击次数
问题描述
我正在构建一个可以对剧集进行分类的播客目录。为播客生成页面的代码、剧集列表以及每个剧集中的类别列表。
播客可以有“体育”和“新闻”类别。一集可能有“体育”,而另一集可能有“体育”和“新闻”作为类别。
问题:我在循环中循环攻击数据库。我知道有一种更有效的方法。
以下是模型的连接方式:
class Podcast(models.Model):
...
class Category(models.Model):
podcast = models.ForeignKey(Podcast, on_delete=models.CASCADE)
...
class Episode(models.Model):
podcast = models.ForeignKey(Podcast, on_delete=models.CASCADE)
categories = models.ManyToManyField(Category, blank=True)
...
然后,当我加载播客页面并想要在其中放置类别和剧集列表时:
class Podcast(models.Model):
...
@property
def get_categories(self):
return self.category_set.all()
@property
def episodes_by_category(self):
episodes_by_category = []
for category in self.get_categories:
episodes = self.episode_set.all().filter(categories=category)
episodes_by_category.append({
'category': category,
'episodes': episodes,
'count': len(episodes),
})
sorted_episodes_by_category = sorted(episodes_by_category, key = lambda i: i['count'],reverse=True)
return sorted_episodes_by_category
然后,当我加载一个剧集页面时,我会显示共享相同类别的其他剧集:
class Episode(models.Model):
podcast = models.ForeignKey(Podcast, on_delete=models.CASCADE)
categories = models.ManyToManyField(Category, blank=True)
...
@property
def get_categories(self):
return self.categories.all().prefetch_related('episode_set').select_related('podcast')
@property
def related_episodes_by_category(self):
episodes_by_category = []
for category in self.get_categories:
episodes = category.episode_set.all().select_related('podcast')
episodes_by_category.append({
'category': category,
'episodes': episodes,
'count': len(episodes),
})
sorted_episodes_by_category = sorted(episodes_by_category, key = lambda i: i['count'],reverse=True)
return sorted_episodes_by_category
以下是我在播客页面上的模板中使用它的方法:
{% for episode_batch in podcast.episodes_by_category %}
<div class="sidebar-object mb-2 pb-2" id="categories">
<div class="section-title section-title--style-1 mb-0">
<h3 class="section-title-inner heading-sm strong-600 text-uppercase">
Top <a href="{{episode_batch.category.get_absolute_url_cached|check_custom_domain:request}}" class="link">{{episode_batch.category.name|title}}</a> Episodes
</h3>
</div>
<ul class="categories list-border--dotted">
{% for episode in episode_batch.episodes %}
{% if forloop.counter < 3 %}
<li>
<a href="{{episode.get_absolute_url_cached|check_custom_domain:request}}">{{episode.name}}</a>
</li>
{% endif %}
{% endfor %}
</ul>
</div>
{% endfor %}
以下是我如何在剧集页面的模板中使用它:
{% for episode_batch in episode.related_episodes_by_category %}
{% if episode_batch.category in episode_categories %}
<div class="sidebar-object mb-2 pb-2" id="categories">
<div class="section-title section-title--style-1 mb-0">
<h3 class="section-title-inner heading-sm strong-600 text-uppercase">
Top <a href="{{episode_batch.category.get_absolute_url_cached|check_custom_domain:request}}" class="link">{{episode_batch.category.name|title}}</a> Episodes
</h3>
</div>
<ul class="categories list-border--dotted">
{% for episode in episode_batch.episodes %}
{% if forloop.counter < 3 %}
<li>
<a href="{{episode.get_absolute_url_cached|check_custom_domain:request}}">{{episode.name}}</a>
</li>
{% endif %}
{% endfor %}
</ul>
</div>
{% endif %}
{% endfor %}
我意识到我可以通过缓存来减少数据库负载,但在我这样做之前,我很想优化我获取这些信息的方式。目前,对于一个有 10 个类别的播客,我每页加载 70-100 次数据库。
解决方案
在重写和进行一些较小的优化之间有一个平衡
在下面,我以剧集模型属性/模板为例进行了一些小改动。您可以走得更远,但这会使 episode_batch 完好无损
模型:
from django.db.models import Count
class Episode(models.Model):
podcast = models.ForeignKey(Podcast, on_delete=models.CASCADE)
categories = models.ManyToManyField(Category, blank=True)
...
@property
def get_categories(self):
#add an annotation to the get categories so you hit the database once for all counts
categories = self.categories.annotate(episode_count=Count('episode')).prefetch_related('episode_set')
return categories
@property
def related_episodes_by_category(self):
episodes_by_category = []
for category in self.get_categories:
episodes = category.episode_set.all()
#slice the episode queryset instead of using counter in the template
episodes_by_category.append({
'category': category,
'episodes': episodes[0:2],
'count': category.episode_count,
})
sorted_episodes_by_category = sorted(episodes_by_category, key = lambda i: i['count'],reverse=True)
return sorted_episodes_by_category
模板:
{% for episode_batch in episode.related_episodes_by_category %}
<div class="sidebar-object mb-2 pb-2" id="categories">
<div class="section-title section-title--style-1 mb-0">
<h3 class="section-title-inner heading-sm strong-600 text-uppercase">
Top <a href="{{episode_batch.category.get_absolute_url_cached|check_custom_domain:request}}" class="link">{{episode_batch.category.name|title}}</a> Episodes
</h3>
</div>
<ul class="categories list-border--dotted">
{% for episode in episode_batch.episodes %}
<li>
<a href="{{episode.get_absolute_url_cached|check_custom_domain:request}}">{{episode.name}}</a>
</li>
{% endif %}
{% endfor %}
</ul>
</div>
{% endif %}
{% endfor %}
笔记:
删除了以下行,我看不到“episode_categories”的定义位置,但如果您确实需要进一步限制,请将其移至视图/属性
{% if episode_batch.category in episode_categories %}
删除了以下
{% if forloop.counter < 3 %}
推荐阅读
- typescript - 打字稿模板类 typeof
- linux - Cassandra - 内存不足(大型服务器)
- google-app-engine - 我可以将哪个自动缩放数据库与 Google App Engine 一起使用
- php - 如何通过payeer支付API显示payeer钱包余额?
- ios - 如何在 iOS 上显示使用 firebase-cpp-sdk 收到的 FCM“数据”消息?
- asp.net - 从 Visual Studio 保存页面时,标签结束标记 () 被写入我的 .aspx 页面
- mysql - 一段时间后sql更新自动回滚或更改
- python - 使用自身的功能注释每个查询
- r - 使用函数将结果保存在嵌套循环中
- python - 我怎样才能写原始数据?