Python + Django4 to build a personal blog (18): Sort articles by popularity and time

Table of contents

Statistical article views

Modify model

Modify view

Modify list template

Modify details template

Popularity and time sorting

Modify view

Modify template

Conclusion


 The number of views or article popularity is an important index to evaluate the quality of a blog post. Generally, blog websites have the function of statistics and sorting by page views.

In this article, we will implement blog browsing statistics and add the function of sorting by popularity and time.

Statistical article views

Modify model

To implement the pageviews function, we first need to add a pageviews field.

So modify the model of the article:

# 博客文章数据模型
class Article(models.Model):
    # 文章id,主键
    id = models.AutoField(primary_key=True)

    # 文章作者。修改为User的外键,参数 on_delete 用于指定数据删除的方式
    author = models.ForeignKey(User, on_delete=models.CASCADE)

    # 文章标题,models.CharField 为字符串字段,用于保存较短的字符串,比如标题
    title = models.CharField('标题',max_length=100)

    # 文章正文,保存大量文本使用 TextField
    body = models.TextField('文章正文')

    # 文章创建时间,参数 default=timezone.now 指定其在创建数据时将默认写入当前的时间
    created = models.DateTimeField(default=timezone.now)
    #增加浏览量字段
    total_views = models.PositiveIntegerField(default=0)

    # 文章更新时间,参数 auto_now=True 指定每次数据更新时自动写入当前时间
    updated = models.DateTimeField(auto_now=True)
    # 获取文章地址
    def get_absolute_url(self):
        return reverse('detail', args=[self.id])
  • PositiveIntegerFieldis a field used to store positive integers
  • default=0Set initial value starting from 0

Migrate data

python manage.py makemigrations

python manage.py migrate

Modify view

What we need to achieve is that every time a user visits the details page, the number of views is increased by 1.

Therefore the modification article_detail()is as follows:

# 文章详情
def article_detail(request,id):
    # 取出相应的文章
    article = Article.objects.get(id=id)
    # 浏览量 +1
    article.total_views += 1
    article.save(update_fields=['total_views'])
    # 取出文章评论
    comments = Comment.objects.filter(article=id)
    # 需要传递给模板的对象
    context = {'article': article, 'comments': comments}
    # 载入模板,并返回context对象
    return render(request, 'article/detail.html', context)

update_fields=[]Specifies that the database only updates total_viewsfields to optimize execution efficiency.

Modify list template

First, we add a reading display to each article card in the list.

Here we add a reading icon for the sake of aesthetics.

To use icons we need to introduce the icon library, here we introduce it through online form.

templates/base.htmlAdd the following code in :

<!--    载入静态文件-->
{% load static %}

<!DOCTYPE html>
<!-- 网站主语言 -->
<html lang="zh-cn">
  <head>
    <!-- 网站采用的字符编码 -->
    <meta charset="utf-8">
    <!-- 预留网站标题的位置 -->
    <title>{% block title %}{% endblock %}</title>
    <!-- 引入bootstrap的css文件  -->
    <link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}">
    <!-- 引入图标库 -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.css">
  </head>

Then templates/article/list.htmladd the following code in:

      	...
						<div class="card-body">
                    <h4 class="card-title">{
   
   { article.title }}</h4>
                    <br>
                    <p class="card-text">{
   
   { article.body|slice:'100' }}...</p>
                    <a href="{% url 'detail' article.id %}"  class="card-link">阅读本文</a>
                    <!-- 这里增加阅读量和图标 -->
                        <small class="col align-self-end" style="color: gray;">
                            <span class="bi bi-eye">
                            {
   
   { article.total_views }}
                            </span>
                        </small>
                </div>

The effect is as follows:

Modify details template

 

Similarly, we also add icons and reading volume behind the title of the details page.

<div class="col-12 alert alert-primary">
            <div class="col-12">
                <a>作者:{
   
   { article.author }}</a>
                &nbsp
                <a>{
   
   { article.created|date:'Y-m-d H:i:s' }}</a>
                &nbsp
                <!-- 只有作者可以修改文章 -->
                {% if user == article.author %}
                <a href="#" data-bs-toggle="modal" data-bs-target="#myModal">删除文章</a>
                <!-- 新增一个隐藏的表单 -->
                <form
                        style="display:none;"
                        id="safe_delete"
                        action="{% url "delete" article.id %}"
                        method="POST"
                >
                    {% csrf_token %}
                    <button type="submit">发送</button>
                </form>
                &nbsp
                <a href="{% url "update" article.id %}">编辑文章</a>
                {% endif %}
               <!-- 增加阅读量和图标 -->
                <small class="col align-self-end" style="color: gray;">
                            <span class="bi bi-eye">
                            {
   
   { article.total_views }}
                            </span>
                </small>
            </div>
        </div>

In the above code, we added the reading volume and icon after the edited article. The effect is as follows:

Popularity and time sorting

After adding the pageviews field, we continue to implement the sorting function.

Modify view

Our sorting function is mainly on the list page, so we rewrite the following article_listview:

def article_list(request):
    # 根据GET请求中查询条件
    # 返回不同排序的对象数组
    if request.GET.get('order') == 'total_views':
        article_list = Article.objects.all().order_by('-total_views')
        order = 'total_views'
    else:
        article_list = Article.objects.all().order_by('created')
        order = 'created'

    paginator = Paginator(article_list, 2)
    page = request.GET.get('page')
    articles = paginator.get_page(page)

    # 修改此行
    context = { 'articles': articles, 'order': order }

    return render(request, 'article/list.html', context)

The key code of the view function is explained as follows:

  • In the Get request, we add a passing parameter order, and the view sorts the retrieved article objects by different fields according to the requested parameters.
  • Here we only have two sorting options, by views and by creation date. The default is by creation date.
  • order_by('-total_views'), order_by()is the sorting method. Here we add a negative sign in front of the integer field, which means we sort the fields in reverse order (only signed fields can do this).
  • context = { 'articles': articles, 'order': order }, when we pass the parameters here, we will orderalso pass them together, because we will have paging action. When paging, there is no need to click the sort button again. You need to specify the current sorting method.

Modify template

Next, modify the list template. You need to modify two places:

First, we add two sorting options at the top of the list. Here we use Bootstrapthe breadcrumb navigation component.

<!-- 写入 base.html 中定义的 content -->
{% block content %}
<!-- 定义放置文章标题的div容器 -->
 <br>
<div class="container">
    <nav aria-label="breadcrumb">
        <ol class="breadcrumb">
            <li class="breadcrumb-item">
                <a href="{% url 'list' %}">
                    最新
                </a>
            </li>
            <li class="breadcrumb-item">
                <a href="{% url 'list' %}?order=total_views">
                    最热
                </a>
            </li>
        </ol>
    </nav>

The effect is as follows:

Then we also add orderparameters to the pagination jump link:

<div class="pagination row">
    <div class="m-auto">
        <span class="step-links">
            <!-- 如果不是第一页,则显示上翻按钮 -->
            {% if articles.has_previous %}
                <a href="?page=1&order={
   
   { order }}" class="btn btn-success">
                    &laquo; 1
                </a>
                <span>...</span>
                <a href="?page={
   
   { articles.previous_page_number }}&order={
   
   { order }}"
                   class="btn btn-secondary"
                >
                    {
   
   { articles.previous_page_number }}
                </a>
            {% endif %}
            <!-- 当前页面 -->
            <span class="current btn btn-danger btn-lg">
                {
   
   { articles.number }}
            </span>
            <!-- 如果不是最末页,则显示下翻按钮 -->
            {% if articles.has_next %}
                <a href="?page={
   
   { articles.next_page_number }}&order={
   
   { order }}"
                    class="btn btn-secondary">{
   
   { articles.next_page_number }}</a>
                <span>...</span>
                <a href="?page={
   
   { articles.paginator.num_pages }}&order={
   
   { order }}"
                    class="btn btn-success">{
   
   { articles.paginator.num_pages }} &raquo;</a>
            {% endif %}
        </span>
    </div>
</div>

Conclusion

In this article, we have added the function of counting page views, and on this basis, we have also added the function of sorting the blog post list, which can sort articles according to the latest or the hottest.

In addition, we introduced the eye icon to vividly display the reading effect.

In the next article, we will implement the last core function of the blog website: search.

Guess you like

Origin blog.csdn.net/agelee/article/details/127264630