本部分內容參考B站UP再敲一行代碼的Django2教程[1]
第十八課-熱門博客閱讀及緩存提速在上節課中,我們討論了閱讀計數統計和顯示的問題,我們可以對這個進一步挖掘,統計當日熱門閱讀博客、昨日熱門閱讀博客以及最近七天熱門閱讀博客。這是本文的第一部分內容,第二部分將針對七天熱門閱讀博客,使用緩存來進行提速。(這裡我發現上一篇博客的問題:應該要展示前七天的閱讀數,因為當日的閱讀數是在不斷更新的,所以展示的話需要使用前七天的數據。在對日期進行控制的時候,range迭代器應該是range(7,0, -1))
1. 熱門博客閱讀關於熱門博客閱讀統計的實現,可以按照以下邏輯:根據時間獲取對應時間段博客的閱讀數並按照閱讀數倒序排列。
在read_count/utils.py中新建一個get_today_hot_data方法,先通過content_type以及date進行過濾,獲取當日博客的閱讀數,然後進行按照閱讀數降序排列。需要注意,得到的read_details是一個QuerySet,可以通過對其切片獲取排名靠前的若干條博客。
def get_today_hot_data(content_type):
today = timezone.now().date()
read_details = ReadDetail.objects.filter(content_type=content_type, date=today) \
.order_by('-read_num')
return read_details因為這個內容顯示在首頁,所以我們在mysite/views.py中修改,以在首頁顯示這部分內容。向context中添加新內容today_hot_data。
from django.shortcuts import render
from django.contrib.contenttypes.models import ContentType
from read_count.utils import get_seven_days_read_data, get_today_hot_data
from blog.models import Blog
def home(request):
...
context['today_hot_data'] = get_today_hot_data(content_type=blog_content_type)
return render(request, "home.html", context)然後修改home.html的內容。這裡因為得到的read_details是個查詢集合,所以向模版內傳遞的時候,需要對其進行遍歷。這裡我們顯示熱門博客名和博客閱讀數,然後添加連結。因為我們是使用的ReadDetail類模型進行過濾,所以得到的hot_data具有的屬性應該是ReadDetail指定的,通過hot_data.content_object我們可以獲得對應的博客,然後通過pk以及title獲取博客id和博客名。
<h3>今日熱門博客</h3>
<ul>
{% for hot_data in today_hot_data %}
<li>
<a href="{% url 'blog_detail' hot_data.content_object.pk %}">
{{ hot_data.content_object.title }}
</a>
({{ hot_data.read_num }})
</li>
{% empty %}
今天沒有熱門博客
{% endfor %}
</ul>首頁顯示效果如下:
獲取昨日熱門博客排列方法與上述相同,區別在於日期要往前推一天,可以使用today -datetime.timedelta(days=1)獲取前一天日期。
近七天熱門博客排列的獲取則需要取某個時間範圍:日期小於今天,大於等於七天前的日期。這個可以用filter來獲取,這裡出現一個問題:我們獲取到了這個時間範圍內的數據,但是怎麼歸類到每天的情況呢。這時就需要用到分組統計,我們前面統計閱讀數的時候有用到一個函數-aggreagate進行分組聚合。但是要獲得具體的分組,需要使用annotate方法,根據Django官網提供的`values()`方法[2],可以將QuerySet按照傳進去的參數進行分組,然後再使用annotate進行匯總操作。
首先在mystic/utils.py中添加新方法:這裡使用了過濾條件(可以回看第十三課中過濾條件部分的內容)獲取了最近七天(不包括今天)的查詢集,然後使用values根據content_type,object_id進行分組,使用annotate進行求和統計操作,並根據read_num_sum降序排列。
def get_seven_days_hot_data(content_type):
today = timezone.now().date()
date = today - datetime.timedelta(days=7)
read_details = ReadDetail.objects \
.filter(content_type=content_type,date__lt=today,date__gte=date) \
.values('content_type', 'object_id') \
.annotate(read_num_sum=Sum('read_num')) \
.order_by('-read_num_sum')
return read_details[:7]接下來的步驟同上,在mysite/views.py中向context填充新內容,然後在home.html中引用內容(閱讀量為read_num_sum)。這時,會發生如下錯誤:
這是因為在進行values操作時返回的查詢集並不是最後我們需要的read_detail,在最後home.html中我們通過遍歷,使用的是content_object欄位,但是這個欄位並沒有返回,所以會出現這個報錯。我們先使用object_id作為博客id,然後用另一種方法來獲得博客名。
<h3>七日熱門博客</h3>
<ul>
{% for hot_data in seven_days_hot_data %}
<li>
<a href="{% url 'blog_detail' hot_data.object_id %}">
xxx
</a>
({{ hot_data.read_num_sum }})
</li>
{% empty %}
近七天沒有熱門博客
{% endfor %}
</ul>顯示效果如下:
顯示博客名:根據前面的分析,如果我們直接在獲取七天數據的時候,使用Blog模型類進行過濾,那我們就可以直接得到我們七天內的博客查詢集。這裡需要用到Django中的反向關聯[3],可以在blog/models.py中新建一個欄位,用於關聯到ReaddDetail(這個欄位不需要進行資料庫遷移操作),這樣我們可以直接對這個欄位進行篩選。
from django.db import models
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericRelation
from ckeditor_uploader.fields import RichTextUploadingField
from read_count.models import ReadNumExpandMethod, ReadDetail
/.../
class Blog(models.Model, ReadNumExpandMethod):
title = models.CharField(max_length=100)
blog_type = models.ForeignKey(BlogType, on_delete=models.DO_NOTHING)
content = RichTextUploadingField()
read_details = GenericRelation(ReadDetail)
...我們可以先在shell中熟悉這個反向關聯:通過read_details欄位,我們可以從博客訪問到ReadDetail,然後通過雙下劃線獲得ReadDetail中的date欄位,並進行篩選,使用Blog中的id,title進行分組然後求和,即可獲得想要的結果。
>>> from blog.models import Blog
>>> blog=Blog.objects.first()
>>> blog.read_details
<django.contrib.contenttypes.fields.create_generic_related_manager.<locals>.GenericRelatedObjectManager object at 0x7fe96ee7bfd0>
>>> blog.read_details.all()
<QuerySet [<ReadDetail: ReadDetail object (1)>, <ReadDetail: ReadDetail object (3)>]>
>>> import datetime
>>> from django.utils import timezone
>>> from django.db.models import Sum
>>> today=timezone.now().date()
>>> date=today-datetime.timedelta(days=7)
>>> Blog.objects.filter(read_details__date__lt=today, read_details__date__gte=date)
<QuerySet [<Blog: <Blog: 新年第一篇>>, <Blog: <Blog: shell for 30>>, <Blog: <Blog: shell for 30>>]>
>>> blogs = Blog.objects.filter(read_details__date__lt=today, read_details__date__gte=date)
>>> blogs
<QuerySet [<Blog: <Blog: 新年第一篇>>, <Blog: <Blog: shell for 30>>, <Blog: <Blog: shell for 30>>]>
>>> blogs.values('id', 'title').annotate(read_num_sum=Sum('read_details__read_num'))
<QuerySet [{'id': 36, 'title': 'shell for 30', 'read_num_sum': 2}, {'id': 37, 'title': '新年第一篇', 'read_num_sum': 23}]>
>>>我們重寫一下獲得七天熱門博客的方法:
from blog.models import Blog
def get_seven_days_hot_blogs():
today = timezone.now().date()
date = today - datetime.timedelta(days=7)
blogs = Blog.objects \
.filter(read_details__date__lt=today, read_details__date__gte=date) \
.values('id', 'title') \
.annotate(read_num_sum=Sum('read_details__read_num')) \
.order_by('-read_num_sum')
return blogs[:7]然後在mystic/views.py中修改這部分內容,最後在home.html中引用模版
<h3>七日熱門博客</h3>
<ul>
{% for hot_blog in seven_days_hot_blogs %}
<li>
<a href="{% url 'blog_detail' hot_blog.id %}">
{{ hot_blog.title }}
</a>
({{ hot_blog.read_num_sum }})
</li>
{% empty %}
近七天沒有熱門博客
{% endfor %}
</ul>當前首頁顯示效果:
將這部分內容放在三個div.hot-data中,並在home.css中修改樣式
div.hot-data {
text-align: center;
margin-top: 1em;
}
2. 緩存提速前面我們統計了不同時間段的博客熱門數量,會發現有些數據會頻繁計算,這樣會影響頁面打開速度。比較好的策略是:使用緩存數據。Django中的緩存[4]包括內存緩存(Memchached、Redis)、資料庫緩存、文件緩存。我們這裡使用資料庫緩存。
首先,在settings.py中設置緩存,這個緩存不是客戶端(瀏覽器)的緩存,而是伺服器(後臺)的緩存。
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
'LOCATION': 'my_cache_table',
}
}在使用緩存表之前,需要先通過python manage.py createcachetable創建,在mystic/views.py中訪問緩存。
首先使用cache.get,如果獲取到的值為None,那麼就使用cache.set創建緩存,參數分別為鍵值、內容以及有效期(按秒計算)
from django.shortcuts import render
from django.contrib.contenttypes.models import ContentType
from django.core.cache import cache
...
def home(request):
blog_content_type = ContentType.objects.get_for_model(Blog)
dates, read_nums = get_seven_days_read_data(content_type=blog_content_type)
# 獲取7天熱門博客的緩存數據
seven_days_hot_blogs = cache.get('seven_days_hot_blogs')
if not seven_days_hot_blogs:
seven_days_hot_blogs = get_seven_days_hot_blogs()
cache.set('seven_days_hot_blogs', seven_days_hot_blogs, 3600)
print('cals')
else:
print('use cache')
...刷新頁面,發現第一次先計算,後續只需要獲取緩存而不需要再次計算。
以上就是本文的全部內容。獲取熱門博客的方法,可以使用反向關聯;使用緩存提高頁面打開速度,下一篇將開始討論評論功能設計和用戶登錄。
參考資料[1]Django2教程: https://space.bilibili.com/252028233
[2]values()方法: https://docs.djangoproject.com/en/3.1/ref/models/querysets/#values
[3]反向關聯: https://docs.djangoproject.com/en/3.1/ref/contrib/contenttypes/
[4]Django中的緩存: https://docs.djangoproject.com/en/3.1/topics/cache/