主題計數器

為了解各討論主題的熱門程度,可以記錄每個討論主題的瀏覽人次。

修改資料模型

首先開啟 forum/topic/models.py,修改 Topic 這個 資料模型的類別,插入第 13 行來新增一個整數欄位,用以記錄瀏覽次數:

# 討論主題 class Topic(models.Model): subject = models.CharField('討論主題', max_length=255) content = models.TextField('內文') author = models.ForeignKey(User, models.CASCADE) created = models.DateTimeField('建立時間', auto_now_add=True) replied = models.DateTimeField('回覆時間', null=True, blank=True) hits = models.IntegerField('瀏覽次數', default=0) def __str__(self): return "{}: {}".format(self.author, self.subject)

在變更過資料模型的欄位定義之後,要記得對資料庫進行更新,請在終端機執行以下指令:

python manage.py makemigrations python manage.py migrate

修改處理視圖

開啟 forum/topic/views.py,修改 TopicView 視圖類別的內容:

# 檢視討論主題 class TopicView(DetailView): model = Topic def get_context_data(self, **kwargs): # 取得回覆資料傳給頁面範本處理 ctx = super().get_context_data(**kwargs) ctx['reply_list'] = Reply.objects.filter(topic=self.object) return ctx def get_object(self): topic = super().get_object() # 取得欲查看的討論主題 topic.hits += 1 # 等同 topic.hits = topic.hits + 1 topic.save() return topic

修改討論主題列表頁面範本

修改 forum/pagetmpl/topic/topic_list.html,插入第 11, 23 - 25 行,在表格中新增瀏覽次數的欄位:

{% extends "base.html" %} {% block content %} <table class="table table-sm"> <thead> <tr class="text-light bg-dark"> <th>發起時間</th> <th>討論主題</th> <th>發起人</th> <th>回覆時間</th> <th>人氣</th> </tr> </thead> <tbody> {% for topic in topic_list %} <tr> <td>{{ topic.created|date:"Y/m/d H:i" }}</td> <td> <a href="{% url 'topic_view' topic.id %}">{{ topic.subject }}</a> </td> <td>{{ topic.author }}</td> <td>{{ topic.replied|date:"Y/m/d H:i" }}</td> <td> <span class="badge badge-primary">{{ topic.hits }}</span> </td> </tr> {% endfor %} </tbody> </table> {% include "pagination.html" %} {% endblock %}

ForeignKey 外(來)鍵的特異功能

在定義資料模型時,若有使用 ForeignKey 型態的欄位,被該欄位參考的資料模型可以自動取得參考它的紀錄組(queryset),不需手動篩選紀錄後再進行處理。以本案例來說,資料模型 Replytopic 欄位為參考資料模型 TopicForeignKey 型態,因此 Django 會自動在資料模型 Topic 裡新增一個名為 reply_set 的屬性,用以表示參考某一筆討論主題(Topic)的回覆意見(Reply)的紀錄組,可以取得與討論主題相關的回覆意見的所有紀錄。

加入回覆次數的資訊

若要在討論主題列表的頁面顯示該主題有多少篇回覆意見的話,不需要像記錄瀏覽次數一樣,在資料模型 Topic 底下新增一個欄位來儲存這個資訊。利用 Django 對 ForeignKey 型態欄位的處理方式,在視圖中可以透過 reply_set.count() 來取得紀錄的數量。

所以不需要修改資料模型,也不用變更處理視圖的定義,直接修改討論主題列表的頁面範本就可以了。

開啟 forum/pagetmpl/topic/topic_list.html,將內容修改如下:

{% extends "base.html" %} {% block content %} <table class="table table-sm"> <thead> <tr class="text-light bg-dark"> <th>發起時間</th> <th>討論主題</th> <th>發起人</th> <th>回覆時間</th> <th>人氣</th> </tr> </thead> <tbody> {% for topic in topic_list %} <tr> <td>{{ topic.created|date:"Y/m/d H:i" }}</td> <td> <a href="{% url 'topic_view' topic.id %}">{{ topic.subject }}</a> </td> <td>{{ topic.author }}</td> <td>{{ topic.replied|date:"Y/m/d H:i" }}</td> <td> <span class="badge badge-primary">{{ topic.hits }}</span> <span class="badge badge-danger">{{ topic.reply_set.count }}</span> </td> </tr> {% endfor %} </tbody> </table> {% include "pagination.html" %} {% endblock %}

簡化討論主題的程式碼

由於 Django 會透過 ForeignKey 的關聯性,自動為資料模型 Topic 新增 reply_set 屬性,因此不需要自己撰寫程式碼來篩選出相關回覆意見的紀錄,再傳遞給頁面範本。

修改 forum/topic/views.py,刪除 TopicViewget_context_data() 方法:

# 檢視討論主題 class TopicView(DetailView): model = Topic def get_object(self): topic = super().get_object() # 取得欲查看的討論主題 topic.hits += 1 # 等同 topic.hits = topic.hits + 1 topic.save() return topic

接著同步調整一下檢視討論主題的頁面範本 forum/pagetmpl/topic/topic_detail.html,修改第 26 行

{% extends "base.html" %} {% block content %} <div class="card"> <div class="card-header d-md-flex justify-content-between"> <div class="h3">{{ topic.subject }}</div> <div class="text-muted"> <small><i class="fas fa-user"></i> {{ topic.author }}</small> <small><i class="fas fa-clock"></i> {{ topic.created|date:"Y/m/d H:i" }}</small> </div> </div> <div class="card-body"> {% if perms.topic.delete_topic %} <a href="{% url 'topic_delete' topic.id %}" class="btn btn-sm btn-danger float-right">刪除主題</a> {% endif %} {{ topic.content|linebreaks }} </div> {% if user.is_authenticated %} <div class="card-footer"> <a href="{% url 'topic_reply' topic.id %}" class="btn btn-primary">回覆討論</a> </div> {% endif %} </div> <hr/> <ul class="list-group"> {% for reply in topic.reply_set.all %} <li class="list-group-item d-md-flex justify-content-between"> <div class="ml-2">{{ reply.content|linebreaks }}</div> <small class="text-muted"> <i class="fas fa-user"></i> {{ reply.author }} <i class="fas fa-clock"></i> {{ reply.created|date:"Y/m/d H:i" }} {% if perms.topic.delete_reply %} <a href="{% url 'reply_delete' reply.id %}" class="btn btn-sm btn-danger">刪除回覆</a> {% endif %} </small> </li> {% endfor %} </ul> {% endblock %}