python - 服务器响应时间比预期长得多
问题描述
我有一个 Django 应用程序,目前在我的本地主机上运行。有些页面需要更长的时间,因此出于调试原因,我使用以下简单代码计算了在一个基于类的视图中花费的时间:
class DiscoverView(LoginRequiredMixin, ListView):
model = MyModel
template_name = "app1/discover.html"
context_object_name = 'influencers' # not necessary, default is object_list
def get_context_data(self, **kwargs):
start_time = time.time()
# ...
# Some tasks which I tought they are the reasons of long response time
# ...
print(time.time() - start_time, "seconds took to complete...")
return context
在实验之后,我看到这个视图需要0.01 秒才能完成,而页面加载大约需要 5 秒。
为了进一步调试,我深入研究了 Chrome 开发工具。在网络选项卡中,页面的“完成”时间为 5.86 秒。我还运行了 Lighthouse 测试,它表明服务器的初始响应时间约为 2 秒。
我不明白为什么这个 0.01 秒变成了 2 秒,我还使用 django-debug-toolbar 来检查数据库查询时间,这并不长。我的应用程序也在生产(Heroku)上,加载时间当然要差得多,但我觉得我需要先在本地解决这个问题。
感谢您提供任何帮助或调试建议,如果您需要有关应用程序/系统的更多信息,请告诉我。
编辑:这是里面get_context_data()
:
def get_context_data(self, **kwargs):
start_time = time.time()
context = super().get_context_data(**kwargs)
platform = self.kwargs['platform']
page_no = self.request.GET.get('page') # make it int to multiply when slicing
if page_no is not None: page_no = int(page_no)
ITEMS_PER_PAGE = 10
parents = None
if platform == 'instagram':
# Will implement this later
pass
elif platform == 'tiktok':
tiktok_influencers = TiktokInfluencer.objects.all()
context['platform_counter'] = tiktok_influencers.count()
parents = InfluencerParent.objects.filter(tiktok__id__in = tiktok_influencers)
elif platform == 'youtube':
youtube_influencers = YoutubeInfluencer.objects.all()
context['platform_counter'] = youtube_influencers.count()
parents = InfluencerParent.objects.filter(youtube__id__in = youtube_influencers)
elif platform == 'twitch':
twitch_influencers = TwitchInfluencer.objects.all()
context['platform_counter'] = twitch_influencers.count()
parents = InfluencerParent.objects.filter(twitch__id__in = twitch_influencers)
elif platform == 'clubhouse':
clubhouse_influencers = ClubhouseInfluencer.objects.all()
context['platform_counter'] = clubhouse_influencers.count()
parents = InfluencerParent.objects.filter(clubhouse__id__in = clubhouse_influencers)
else:
# unknown platform, redirect to 404
pass
# Pagination stuff
if page_no == None or page_no == 1:
parents = parents[:ITEMS_PER_PAGE]
else:
parents = parents[(page_no-1) * ITEMS_PER_PAGE : (page_no) * ITEMS_PER_PAGE]
context['influencers'] = parents
context['platform'] = platform
print(time.time() - start_time, "seconds took DiscoverView to complete...")
return context
编辑 2:我需要进一步的帮助,所以我要添加模型和模板信息:
# models.py
class InfluencerParent(models.Model):
'''
Parent class which points to influencers' IG, YT & other social media platform accounts
'''
def __str__(self):
if self.instagram.first():
return self.instagram.first().fullname
else:
return "None"
class InstagramInfluencer(models.Model):
# some fields...
influencer_parent = models.ForeignKey(InfluencerParent, on_delete=models.SET_NULL, related_name='instagram', default=None, blank=True, null=True)
class YoutubeInfluencer(models.Model):
# some fields...
influencer_parent = models.ForeignKey(InfluencerParent, on_delete=models.SET_NULL, related_name='instagram', default=None, blank=True, null=True)
class TiktokInfluencer(models.Model):
# some fields...
influencer_parent = models.ForeignKey(InfluencerParent, on_delete=models.SET_NULL, related_name='instagram', default=None, blank=True, null=True)
class TwitchInfluencer(models.Model):
# some fields...
influencer_parent = models.ForeignKey(InfluencerParent, on_delete=models.SET_NULL, related_name='instagram', default=None, blank=True, null=True)
class ClubhouseInfluencer(models.Model):
# some fields...
influencer_parent = models.ForeignKey(InfluencerParent, on_delete=models.SET_NULL, related_name='instagram', default=None, blank=True, null=True)
模板样本:
# template (not the all for the sake of clarity)
<!-- discover.html
A single template to list all influencers (IG, YT, Tiktok...)
Takes platform param from URL
In view we filtered parents according to the platform
For example if platform param is 'tiktok' I use this updated version:
parents = InfluencerParent.objects.filter(tiktok__isnull=False).prefetch_related('instagram', 'youtube', 'tiktok', 'twitch', 'clubhouse')
There parents influencers are who have tiktok accounts, in template I show all the social media cards in a tabbed design
So I need to send parent and acces parent.instagram.first.username
-->
{% for influencer in influencers %}
<div class="instagram" {% influencer.instagram.first == None %} disabled {% endif %}>
{{influencer.instagram.first.username}}
</div>
<div class="tiktok" {% influencer.tiktok.first == None %} disabled {% endif %}>
{{influencer.tiktok.first.username}}
</div>
<!--
.
.
OTHER SOCIAL MEDDIA ACCOUNTS OF THAT PARENT
.
.
-->
<div class="clubhouse" {% influencer.clubhouse.first == None %} disabled {% endif %}>
{{influencer.clubhouse.first.username}}
</div>
这是我如何使用 prefetch_related (select_related 给出错误):
elif platform == 'tiktok':
context['platform_counter'] = TiktokInfluencer.objects.count()
parents = InfluencerParent.objects.filter(tiktok__isnull=False).prefetch_related('instagram', 'youtube', 'tiktok', 'twitch', 'clubhouse')
# This is how I use prefetch_lated(), I need to send parent objecjts and their all (if exists) platform related influencer objects
# I acces Parent's all existing reverse foreign key fields in one template, for example:
# {{parent.influencer.username}}
# {{parent.youtube.follower_count}}
# {{parent.tiktok.fullname}}
# ...
# The row below gives error: 'Invalid field name(s) given in select_related: 'tiktok'. Choices are: (none)'
# Even tho I need to give all social platforms as params (select_related('instagram', 'youtube', 'tiktok', 'twitch', 'clubhouse'))
parents = InfluencerParent.objects.select_related('tiktok')
解决方案
https://docs.djangoproject.com/en/3.2/ref/models/querysets/
为了帮助建立基础, select_related用于具有 ForeignKey 关系的模型,通常prefetch_related用于具有 M2M 的模型。在这里,我将提供一种“简单”的方式来实现prefetch_related。请记住,连接到数据库的调用不仅在模板中views.py
,而且在模板内部,访问属性可能对数据库造成额外的打击(请参阅上面的链接以获得进一步的解释,即 blog.author.hometown)。因此,为了减轻我们访问数据库的次数,我建议实施类似的方法。现在没有看到模型、模板和实际的 SQL 查询,这将是我最好的猜测。
...
if platform == 'instagram':
# Will implement this later
pass
elif platform == 'tiktok':
context['platform_counter'] = TiktokInfluencer.objects.count()
parents = InfluencerParent.objects.prefetch_related('tiktok')
elif platform == 'youtube':
context['platform_counter'] = YoutubeInfluencer.objects.count()
parents = InfluencerParent.objects.prefetch_related('youtube')
... repeat this pattern for others
else:
我认为只需添加我所做的编辑就会大大改变查询量。
如果你想看看它会如何变化。取一部分视图,在 shell 中做一些测试
from django.db import reset_queries
from django.db import connection
>>> parents = list(InfluencerParent.objects.prefetch_related('tiktok'))
>>> connection.queries
>>> reset_queries()
>>> parents = list(InfluencerParent.objects.select_related('tiktok'))
>>> connections.queries
# If both work, then your using a Forgeign Key but more importantly
# notice how many SQL queries are made. If using select_related breaks
# its because your using an M2M. But again, the importants is getting that list of "connections" as low as possible
现在为了进一步完善您的代码,我将看看Prefetch ,因为您可以进行类似于您之前实现的缓存查询。
from django.db.models import Prefetch
>>> qs = MyModel.objects.filter(some_lookup)
>>> results = MyDesiredResultObjects.objects.prefetch_related(Prefetch('some_attr'), queryset=qs, to_attr='my_new_result')
>>> my_new_result
============================
既然你已经提供了模型
select_related
...
elif platform == 'tiktok':
context['platform_counter'] = TiktokInfluencer.objects.count()
parents = InfluencerParent.objects.select_related('tiktok')
推荐阅读
- php - 使用php从mysql的html数据中下拉列表
- javascript - 带有 npm / 代码更改的 Kotlin2JS 未反映
- geolocation - Google Play 服务 - 当前位置
- php - ajax json 响应解析错误和语法错误
- deep-learning - 使用Pytorch的densenet时如何获取概率?
- sql - 无法进行 SQL 查询
- android - 如何将“LineChart”中的图标设置到 MPAndroidChart 中的选定位置?
- ethereum - Genesis.json 添加 kovan 或 ropsten 网络
- java - 仅在 oop 中将方法的范围限制为另一个类
- django - Django+postgres auth_user 重复键值违反唯一约束“auth_user_username_key”