您好我创建了一个函数,它将根据创建时间返回帖子,并根据他们的投票数进行排名。这是一个饲料。我不确定如果这是一种有效的方法来做到这一点,重要的是我做对了,因为这可能会筛选成千上万的帖子。我在这里简化了一些,并简要介绍了每一步。我目前的担忧和整个职能的职位(没有注释)都在追随。
开始时间参数是指发布帖子的时间。例如,如果startHours = 6,则仅返回6小时前创建的帖子。
def rank(request, startHours):
首先,我通过投票命令所有帖子
unorderedPosts=Post.objects.order_by('-votes')
然后根据用户指定的类别排除帖子
if request.user.is_authenticated():
preferences=request.user.categorypreference_set.filter(on=False)
for preference in preferences:
unorderedPosts=unorderedPosts.exclude(category_name=preference.category_name)
然后我制作endHours,它总是比startHours参数提前4小时
endHours=startHours+4 #4 hour time window
现在我切片无序的Posts并只获得在时间窗口startHours中创建的那些从现在开始的endHours。例如,如果startHours = 4,则仅返回4小时前但8小时前创建的帖子。
posts=unorderedPosts.filter(created__gte=(timezone.now()-datetime.timedelta(hours=endHours)))
posts=posts.exclude(created__gte=(timezone.now()-datetime.timedelta(hours=startHours)))
现在我创建一个循环,将时间窗口移回,直到找到至少一个具有适合时间窗口的创建日期的帖子。我创建了check变量以防止无限循环(如果在200小时内没有找到帖子,则循环将退出)。
count=posts.count()
check=endHours
while count<1 and endHours<(check+200):
endHours+=4
startHours+=4
posts=unorderedPosts.filter(created__gte=(timezone.now()-datetime.timedelta(hours=endHours)))
posts=posts.exclude(created__gte=(timezone.now()-datetime.timedelta(hours=startHours)))
count=posts.count()
if count>=1: return posts, endHours
return posts
我最关心的是在开头创建所有帖子的查询集。此功能是为了在小时间窗口中返回帖子,是否会通过对数据库中的所有帖子进行排名来不必要地放慢速度?我知道django / python查询集非常有效但是为了这个函数的目的,不会对包含数千个对象的集合进行排序很麻烦吗?
如果这是一个问题,如何在保持一切可访问的同时提高效率?
以下是整篇文章。
def rank(request, startHours):
unorderedPosts=Post.objects.order_by('-upVote')
if request.user.is_authenticated():
preferences=request.user.categorypreference_set.filter(on=False)
for preference in preferences: #filter by category preference
unorderedPosts=unorderedPosts.exclude(category_name=preference.category_name)
endHours=startHours+4 #4 hour time window
posts=unorderedPosts.filter(created__gte=(timezone.now()-datetime.timedelta(hours=endHours)))
posts=posts.exclude(created__gte=(timezone.now()-datetime.timedelta(hours=startHours)))
count=posts.count()
check=endHours
while count<1 and endHours<(check+200):
endHours+=4
startHours+=4
posts=unorderedPosts.filter(created__gte=(timezone.now()-datetime.timedelta(hours=endHours)))
posts=posts.exclude(created__gte=(timezone.now()-datetime.timedelta(hours=startHours)))
count=posts.count()
if count>=1: return posts
return posts
答案 0 :(得分:1)
您主要担心的不是您需要担心的事情。检查When querysets are evaluated上的文档 - 您可以无限期地定义和添加子句到查询集,并且在您调用实际需要访问数据库的内容之前,它实际上不会针对数据库运行。
需要多个查询的内容是迭代时间,直到您点击一个有帖子的窗口。如果您在一次通话中检查最新的created
时间,使用它来计算您的窗口,然后根据该时间限制帖子,然后按投票数排序,您将获得更好的表现。
类似的东西:
unorderedPosts = Post.objects.all()
if request.user.is_authenticated():
preferences=request.user.categorypreference_set.filter(on=False)
for preference in preferences: #filter by category preference
unorderedPosts=unorderedPosts.exclude(category_name=preference.category_name)
latest_post_datetime = unorderedPosts.aggregate(Max('created'))['created__max']
original_start_time = datetime.datetime.now() - datetime.timedelta(hours=startHours)
latest_post_day_start_time = datetime.datetime.combine(latest_post_datetime.date(), original_start_time.time())
# a timedelta guaranteed to be less than 24 hours
time_shift = latest_post_day_start_time - latest_post_datetime
timewindow = datetime.timedelta(hours=4)
if time_shift.days >= 0:
extra_windows_needed = time_shift.seconds / timewindow.seconds
else:
# negative timedeltas store negative days, then positive seconds; negate
extra_windows_needed = -(abs(time_shift).seconds) / timewindow.seconds
start_time = latest_post_day_start_time - (timewindow * (extra_windows_needed + 1))
posts = unorderedPosts.filter(created__gte=start_time).order_by('-upVote')
return posts
只要您的窗口(4)中的小时数均匀分配到当天,此处的数学运算是正确的 - 否则计算正确的偏移量会变得更加棘手。基本上,你需要花费时间偏移mod时间窗口长度,并且我正在利用这样一个事实,即如果你在同一个日历日结束,我知道mod四小时的部分工作。
此外,它不包括结束时间,因为您的原始逻辑在初始startHours
期间不强制执行。它只会将开始时间从其中移出,如果其中没有,那么你不必担心最近出现的东西。
此版本最多可生成三个数据库查询 - 一个用于获取已登录用户的类别首选项,一个用于获取latest_post_datetime
,另一个用于获取posts
,并且至少可以获得一个匹配交。
您可能还会考虑进行性能分析,以查看您的数据库后端是否更好地使用子查询来排除不需要的类别:
if request.user.is_authenticated():
unorderedPosts = unorderedPosts.exclude(category_name__in=request.user.categorypreference_set.filter(on=False).values_list('category_name')
作为__in lookup docs注释,此处的性能因数据库后端不同而不同。