【发布时间】:2020-01-03 11:59:48
【问题描述】:
我们将 Django 2.1 用于Speedy Net。我的页面每页显示大约 96 个用户,对于每个用户,我想显示他在 Speedy Match 上有多少朋友,以及一个有效的电子邮件地址。如果(self.email_addresses.filter(is_confirmed=True).exists()) 为真,查询会检查每个用户:
def has_confirmed_email(self):
return (self.email_addresses.filter(is_confirmed=True).exists())
对于 96 个用户中的每个用户,它会检查他的所有朋友并运行此查询 - 每页超过数百次。获取用户的查询是User.objects.all().order_by(<...>),然后对每个用户检查这个查询:
qs = self.friends.all().prefetch_related("from_user", "from_user__{}".format(SpeedyNetSiteProfile.RELATED_NAME), "from_user__{}".format(SpeedyMatchSiteProfile.RELATED_NAME), "from_user__email_addresses").distinct().order_by('-from_user__{}__last_visit'.format(SiteProfile.RELATED_NAME))
我在用户管理器模型中添加了prefetch_related:
def get_queryset(self):
from speedy.net.accounts.models import SiteProfile as SpeedyNetSiteProfile
from speedy.match.accounts.models import SiteProfile as SpeedyMatchSiteProfile
return super().get_queryset().prefetch_related(SpeedyNetSiteProfile.RELATED_NAME, SpeedyMatchSiteProfile.RELATED_NAME, "email_addresses").distinct()
但是将“email_addresses”和“from_user__email_addresses”添加到prefetch_related 并不能加快页面加载速度 - 加载页面大约需要 16 秒。当加载页面而不检查每个朋友是否有确认的电子邮件地址时,加载页面大约需要 3 秒。有没有办法可以一次加载用户的所有电子邮件地址,而不是每次检查用户时?实际上,我还希望朋友查询被加载一次,而不是每页 96 次(每个用户一次),但是页面在 3 秒内加载,所以没关系。但如果我能查询一下朋友表就更好了。
查询是由以下行(link)引起的:
if ((self.user.has_confirmed_email()) and (step >= self.activation_step)):
这是由is_active_and_valid 调用的get_matching_rank 调用,以检查用户是否与特定用户匹配。这是由模型中的方法get_friends 调用的。
更新 #1:如果我在模型中将 def has_confirmed_email(...) 更改为 return True,页面加载速度仅快 3 秒(13 秒而不是 16 秒),因此可能会有更多与性能相关的问题此页面中的问题。
如果我禁用get_matching_rank 的功能并将其替换为普通的return 5,页面加载速度会更快。但是我们当然需要这个函数的功能。也许我们可以在为两个特定用户集调用此函数时将结果缓存几分钟?
更新#2:我想在用户模型中添加一个布尔字段,如果用户有一个确认的电子邮件地址,这将是真的。每次保存或删除电子邮件地址时,都会更新此字段。我知道如何覆盖保存方法,但是当电子邮件地址被删除时如何更新此字段?它也可能被管理员删除。
我认为我应该使用post_save 和post_delete 等信号。
【问题讨论】:
-
为此实现某种缓存或中间存储会更好吗?您为此询问了很多数据库,可能是时候寻找此类数据的替代品了
-
@Jason 是的,我同意,电子邮件地址可能会被确认或删除,只有这些时候
has_confirmed_email的值会发生变化。无法使已确认的电子邮件地址变为未确认。
标签: python django query-performance prefetch