Django第十八課

2021-03-02 多肉羅羅

本部分內容參考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/

相關焦點

  • Django第二十八課
    這裡大概有兩種方法:一是繼承django的用戶模型;二是使用新的模型拓展關聯User(🌟🌟🌟🌟🌟)。1. 繼承Django的用戶模型我們之前使用的User是在django.contrib.auth.models中引用的,我們可以查看這部分源碼,分析這個User是如何創建的。
  • Django分頁完整示例
    在django中可以使用兩種方法進行分頁,第一種方法是使用基於函數的視圖,第二種方法是使用基於類的視圖。現在,首先,需要使用此命令創建一個新的django項目。我稱這個項目為MyProjectdjango-admin startproject ProjectName首先,需要將目錄更改為已創建的項目,然後需要創建一個App,我將其稱為MyApp。
  • Django&DataTables應用
    如何把資料庫中的數據以表格的形式展示到前端,實現有很多方法,最近用jquery的datatables插件來實現發現還是比較簡單的,今天我們來看一個例子,來說明這個插件的使用,基本原理是view函數從資料庫中讀出數據,jquery通過ajax獲取數據並在前端展示出來,我們先定義一個models.py,如下:from django.db
  • 一個完整的Django入門指南
    要做到這一點,打開settings.py並嘗試找到INSTALLED_APPS變量:INSTALLED_APPS = [    'django.contrib.admin',    'django.contrib.auth',    'django.contrib.contenttypes',    'django.contrib.sessions
  • python程式設計師嘔心瀝血整理 Django 優秀資源大全
    django-templated-email, star:291 - 一個 Django 模板,能很容易地發送模板型郵件,可以使用 django 模板,或事務型郵件提供商(如 mailchimp, silverpop 等)。django-yubin, star:22 - django-mailer2 + django-mailviews,及其它功能。
  • Python Django之路與您同行
    主要在這裡寫下自己的學習筆記、軟體測試思考及讀書感悟等,後續可能會系統的介紹一些python、django、移動端自動化測試、接口自動化測試、性能測試等。歡迎您的光臨!要想深入測試,必須了解功能邏輯,對數據流及網站架構比較清楚,這點也說過多次,真的很重要,必須要體現在工作當中,養成習慣,絕對不要對自己測試過的功能模塊其中的技術實現不清楚。這樣測試路會不好走!!!
  • django-admin和manage.py用法
    記錄要點:django-admin和manage.py 能做同樣的事情 像我們常用的python manage.py runserver,用django-admin也可以操作:django-admim runserver 注意: django-admin需要提前提前配置好DJANGO_SETTINGS_MODULE環境變量
  • Django第二十六課
    模型設計2.1 新建app使用python manage.py startapp likes新建一個likes app2.2 創建模型2.2.1 點讚總數模型點讚總數模型涉及到的是點讚對象類型以及點讚總數,所以設計如下欄位:from django.db
  • Django 2.0 項目實戰: 擴展Django自帶User模型,實現用戶註冊與登錄
    INSTALLED_APPS = [    'reg.apps.RegConfig',    'django.contrib.admin',    'django.contrib.auth',    'django.contrib.contenttypes',    'django.contrib.sessions',    'django.contrib.messages
  • Django官方為什麼沒有標準項目結構
    startproject的完整格式為django-admin startproject name [directory],可以在後面追加一個目錄參數:...\> django-admin startproject helloworld hello-world就可以了。根目錄是hello-world,裡面的project是helloworld。
  • Django實現分頁功能
    Paginator 可以叫它為分頁器,實際上它也是一個 Python 類,要使用它的時候我們可以用如下方式進行引入:from django.core.paginator import Paginator這個類被定義在 django.core.paginator 模塊中,它的構造函數如下所示:class Paginator
  • 向Django Admin添加圖表
    幸運的是,django後臺管理應用程式是可擴展的,通過一些調整,我們可以將交互式Javascript圖表添加到後臺管理中。問題我想獲得findwork.dev上電子郵件訂閱用戶隨時間變化的圖表預覽。就電子郵件用戶而言,該網站是在增長還是停滯不前?上個月我們有多少訂閱用戶?哪個星期我們得到的訂閱用戶最多?所有用戶都在驗證他們的電子郵件嗎?
  • Python之Django文件上傳
    DOCTYPE html><html><head><meta charset="UTF-8"> <title>kahn django上傳文件</title></head><body><form method="post" action="/uploadFiles/" enctype="multipart
  • django 自帶 user 欄位擴展及頭像上傳
    import adminfrom django.contrib.auth.admin import UserAdminfrom django.contrib.auth.models import Userclass ProfileInline(admin.StackInlin):    model = UserProfile    can_delete
  • Python——用 Django 寫 restful api 接口
    models.py (https://code.ziqiangxuetang.com/django/django-models.html)—— 與資料庫操作相關,存入或讀取數據時用到這個,當然用不到資料庫的時候 你可以不使用。
  • Python基礎教程——秒懂django操作資料庫
    Mariadb資料庫,簡單易用、性能高Python和django提供了完善的操作資料庫及事務的方法,首先明確一點,這很簡單,一點也不複雜,下面你會看到,10行代碼就搞定了。現在,先上結論:如果我們只是很簡單的操作一下資料庫,我們推薦使用mysqlclient這個第三方庫如果對資料庫的操作要做一些處理,資料庫又會變動,或者業務需求比較複雜,那就推薦使用django簡易操作代碼實在太簡單,如下所示:
  • 如何使用Django發送電子郵件和附件
    from django.urls import path, include from test_email.views import ( simple_email, email_with_template, email_with_attachment, email_with_custom_attachment_file_name, bulk_email
  • 最淺顯易懂的Django系列教程(1)-URL與視圖
    在用戶輸入了某個url,請求到我們的網站的時候,django會從項目的urls.py文件中尋找對應的視圖。在urls.py文件中有一個urlpatterns變量,以後django就會從這個變量中讀取所有的匹配規則。匹配規則需要使用django.urls.path函數進行包裹,這個函數會根據傳入的參數返回URLPattern或者是URLResolver的對象。
  • Django第十七課
    from django.utils import timezone...比較好的辦法是我們可以參照read_count/utlis.py中的寫法,當天打開則當天添加一條記錄from django.contrib.contenttypes.models import ContentTypefrom django.utils import timezonefrom .models import ReadNum
  • ubuntu18.04 django 連接 mysql 資料庫方法和常見錯誤總結
    本人電腦系統 ubuntu18.04 , django版本 2.2在ubuntu電腦使用django連接mysql資料庫的時候遇到了一些問題