Django個人博客搭建8-優化文章模塊

Django個人博客搭建1-創建Django項目和第一個App
Django個人博客搭建2-編寫文章Model模型,View視圖
Django個人博客搭建3-創建superuser並向數據庫中添加數據並改寫視圖
Django個人博客搭建4-配置使用 Bootstrap 4 改寫模板文件
Django個人博客搭建5-編寫文章詳情頁面並支持markdown語法
Django個人博客搭建6-對文章進行增刪查改
Django個人博客搭建7-對用戶登陸註冊等需求的實現
Django個人博客搭建8-優化文章模塊
Django個人博客搭建9-增加文章評論模塊

1.文章分頁

利用Django內置的分頁模塊:Paginator類(:Paginator官網例子),因爲是對文章分頁,因此需要修改article/views.py中article_list視圖:

from django.core.paginator import Paginator



# 文章列表函數
def article_list(request):
    # 取出所有博客文章
    article_list = ArticlePost.objects.all()
    # 每頁顯示一篇文章
    paginator = Paginator(article_list, 1)
    # 獲取url中的頁碼
    page = request.GET.get('page')
    # 將導航對象相應的頁碼內容返回給articles
    articles = paginator.get_page(page)
    # 需要傳遞給模板對象
    context = {'articles': articles}
    # render函數載入模板,並返回context對象
    return render(request, 'article/list.html', context)
    # article/list.html:模板位置          context:傳入模板的對象

接下來修改templates/article/list.html加入分頁內容

...
<div class="pagination row">
    <div class="m-auto">
        <span class="step-links">
            <!-- 如果不是第一頁,則顯示上翻按鈕 -->
            {% if articles.has_previous %}
                <a href="?page=1" class="btn btn-success">
                    &laquo; 1
                </a>
                <span>...</span>
                <a href="?page={{ articles.previous_page_number }}"
                   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 }}"
                   class="btn btn-secondary"
                >
                    {{ articles.next_page_number }}
                </a>
                <span>...</span>
                <a href="?page={{ articles.paginator.num_pages }}"
                   class="btn btn-success"
                >
                    {{ articles.paginator.num_pages }} &raquo;
                </a>
            {% endif %}
        </span>
    </div>
</div>

最後查看效果:
在這裏插入圖片描述
在這裏插入圖片描述
分頁功能完成

2.文章瀏覽量

首先修改文章的模型article/models.py

# 博客文章數據模型
class ArticlePost(models.Model):
...
    updated = models.DateTimeField(auto_now=True)
    total_views = models.PositiveIntegerField(default=0)
...

PositiveIntegerField是用於存儲正整數的字段
接着生成遷移

python manage.py makemigrations
F:\Desktop\myblog>python manage.py makemigrations
System check identified some issues:

WARNINGS:
article.ArticlePost.created: (fields.W161) Fixed default value provided.
        HINT: It seems you set a fixed date / time / datetime value as default for this field. This may not be what you want. If you want to have the current date as default, use `django.utils.timezone.now`
Migrations for 'article':
  article\migrations\0003_auto_20190210_1956.py
    - Add field total_views to articlepost
    - Alter field created on articlepost
Migrations for 'userprofile':
  userprofile\migrations\0002_auto_20190210_1956.py
    - Alter field avatar on profile

執行遷移:

F:\Desktop\myblog>python manage.py migrate
System check identified some issues:

WARNINGS:
article.ArticlePost.created: (fields.W161) Fixed default value provided.
        HINT: It seems you set a fixed date / time / datetime value as default for this field. This may not be what you want. If you want to have the current date as default, use `django.utils.timezone.now`
Operations to perform:
  Apply all migrations: admin, article, auth, contenttypes, sessions, userprofile
Running migrations:
  Applying article.0003_auto_20190210_1956... OK
  Applying userprofile.0002_auto_20190210_1956... OK

F:\Desktop\myblog>

我們一般需要在文章列表和文章詳情中顯示各個文章的瀏覽量,因此先修改article/list.html

...
<div class="card-footer">
                        <a href="{% url 'article:article_detail' article.id %}" class="btn btn-primary">閱讀本文</a>
                        <span>
                            <small class="col align-self-end" style="color: gray;">
                                瀏覽量:{{ article.total_views }}
                            </small>
                        </span>
                        ...

接着修改詳情模板article/detail.html:

<!-- extends表明此頁面繼承自 base.html 文件 -->
{% extends "base.html" %}
{% load staticfiles %}
<!-- 寫入 base.html 中定義的 title -->
{% block title %}
    文章詳情
{% endblock title %}
<!-- 寫入 base.html 中定義的 content -->
{% block content %}
<!-- 文章詳情 -->
    <div class="container">
       ...
                {% if user == article.author %}
            .<a href="#" onclick="confirm_delete()">刪除文章</a>
            .<a href="{% url "article:article_update" article.id %}">編輯文章</a>
                {% endif %}
            </div>
            <div>
                瀏覽量:{{ article.total_views }}
            </div>
            <div class="col-12">
                <p>{{ article.body|safe }}</p>
            ...
{% endblock content %}

首先修改得就是檢查登陸用戶是否爲文章作者本人,如果是本人才會顯示刪除和編輯文章,接着在下面增加顯示瀏覽量
重啓服務器:

在這裏插入圖片描述
可以看見文章列表可以顯示文章瀏覽量了,點擊閱讀本文:
在這裏插入圖片描述
前面修改了修改刪除文章得權限,因此用戶沒有登陸時時沒有這兩個選項的,我們點擊登陸在這裏插入圖片描述
登陸後就顯示了,但是由於沒有對瀏覽量的進行邏輯處理,就是文章的瀏覽量總是初始給定的0,因此修改article/views.py中article_detail使其點擊一次文章詳情total_views就+1:

def article_detail(request, id):
    ...
    article.total_views += 1
    article.save(update_fields=['total_views'])
	...

update_fields=[]指定了數據庫只更新total_views字段,優化執行效率。
我們重新刷新一下詳情頁面:
在這裏插入圖片描述
發現文章的瀏覽量已經自動增加了
這樣文章瀏覽量功能就已經完成了

3.根據瀏覽量排序最熱文章:

重寫article/views.py:article_list():

# 文章列表函數
def article_list(request):
    # 根據GET請求中查詢條件
    # 返回不同排序的對象數組
    if request.GET.get('order') == 'total_views':
        article_list = ArticlePost.objects.all().order_by('-total_views')
        order = 'total_views'
    else:
        article_list = ArticlePost.objects.all()
        order = 'normal'
    paginator = Paginator(article_list, 3)
    page = request.GET.get('page')
    articles = paginator.get_page(page)
    # 修改此行
    context = {'articles': articles, 'order': order}
    return render(request, 'article/list.html', context)

'-total_views’爲反序,即文章瀏覽量高的在前面
接下來修改templates/article/list.html

...
{% block content %}
<!-- 定義放置文章標題的div容器 -->
    <div class="container">
        <nav aria-label="breadcrumb">
            <ol class="breadcrumb">
            <li class="breadcrumb-item">
                <a href="{% url 'article:article_list' %}">
                    最新
                </a>
            </li>
            <li class="breadcrumb-item">
                <a href="{% url 'article:article_list' %}?order=total_views">
                    最熱
                </a>
            </li>
        </ol>
        </nav>
        <div class="row mt-2">
            {% for article in articles %}
            <!-- 文章內容 -->
            ...

重啓服務器點擊最熱查看效果:
在這裏插入圖片描述
可以看見文章排序是按照文章瀏覽量排序的了
這樣最熱文章功能就完成了

4.搜索文章功能

Q對象
Model.objects.all()能夠返回表中的所有對象。

對應的,Model.objects.filter(**kwargs)可以返回與給定參數匹配的部分對象。

還有Model.objects.exclude(**kwargs)返回與給定參數不匹配的對象

如果想對多個參數進行查詢怎麼辦?比如同時查詢文章標題和正文內容。這時候就需要Q對象。
首先修改article/views.py

# 文章列表函數
def article_list(request):
    search = request.GET.get('search')
    order = request.GET.get('order')
    # 用戶搜索邏輯,請求中有search,則走次邏輯
    if search:
        if order == 'total_views':
        # 用q對象進行聯合搜索
            article_list = ArticlePost.objects.filter(
                Q(title__contains=search)|  # icontains不區分分大小寫,contains區分大小寫
                Q(body__icontains=search)
            ).order_by('-total_views')

        else:
            article_list = ArticlePost.objects.filter(
                Q(title__contains=search) |
                Q(body__icontains=search)
            )
    # 根據GET請求中查詢條件
    # 返回不同排序的對象數組
    else:
        search = ''
        if order == 'total_views':
            article_list = ArticlePost.objects.all().order_by('-total_views')
        else:
            article_list = ArticlePost.objects.all()
    paginator = Paginator(article_list, 3)
    page = request.GET.get('page')
    articles = paginator.get_page(page)
    # 修改此行
    context = {'articles': articles, 'order': order, 'search': search}
    return render(request, 'article/list.html', context)

接着修改模板:article/list.html

 <nav aria-label="breadcrumb">
            <ol class="breadcrumb">
            <li class="breadcrumb-item">
                <a href="{% url 'article:article_list' %}">
                    最新
                </a>
            </li>
            <li class="breadcrumb-item">
                <a href="{% url 'article:article_list' %}?order=total_views">
                    最熱
                </a>
            </li>
        </ol>
        </nav>
        <div class="row">
            <div class="col-auto mr-auto">
                <form class="form-inline">
                    <label class="sr-only">
                        content
                    </label>
                    <input type="text"
                           class="form-control"
                            name="search"
                            placeholder="搜索文章..."
                            required>
                </form>
            </div>
        </div>
        {# 搜索提示語 #}
        {% if search %}
            <h4>
                <span style="color: red">{{ search }}</span>
                的搜索內容如下
            </h4>
            <hr>
            {% else %}
                <h4>暫無<span style="color: red">{{ search }}</span> </h4>
                有關的文章
            <hr>
        {% endif %}
        <div class="row mt-2">
            {% for article in articles %}
            <!-- 文章內容 -->
            <div class="col-4 mb-4">
            <!-- 卡片容器 -->
                <div class="card h-100">
                    <!-- 標題 -->
                    <h4 class="card-header">{{ article.title }}</h4>
                    <!-- 摘要 -->
                    <div class="card-body">
                        <p class="card-text">{{ article.body|slice:'100' }}...</p>
                    </div>
                    <!-- 註腳 -->
                    <div class="card-footer">
                        <a href="{% url 'article:article_detail' article.id %}" class="btn btn-primary">閱讀本文</a>
                        <span>
                            <small class="col align-self-end" style="color: gray;">
                                瀏覽量:{{ article.total_views }}
                            </small>
                        </span>
                    </div>
                </div>
            </div>
            {% endfor %}
        </div>
    </div>

<div class="pagination row">
    <div class="m-auto">
        <span class="step-links">
            <!-- 如果不是第一頁,則顯示上翻按鈕 -->
            {% if articles.has_previous %}
                <a href="?page=1&order={{ order }}&search={{ search }}" class="btn btn-success">
                    &laquo; 1
                </a>
                <span>...</span>
                <a href="?page={{ articles.previous_page_number }}&order={{ order }}&search={{ search }}"
                   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 }}&search={{ search }}"
                   class="btn btn-secondary"
                >
                    {{ articles.next_page_number }}
                </a>
                <span>...</span>
                <a href="?page={{ articles.paginator.num_pages }}&order={{ order }}&search={{ search }}"
                   class="btn btn-success"
                >
                    {{ articles.paginator.num_pages }} &raquo;
                </a>
            {% endif %}
        </span>
    </div>
</div>

麪包屑組件、頁碼組件都改動了href:增加了search參數
新增搜索欄,以GET請求提交search參數;required屬性阻止用戶提交空白文本
新增搜索提示語。好的UI必須讓用戶瞭解當前的狀態
我們重啓服務器打開文章列表頁面
搜索java
在這裏插入圖片描述
這樣文章的搜索功能就完成了

5.渲染Markdown目錄

首先修改article/views.py:

# 文章詳情
def article_detail(request, id):
    ...
    article.body = markdown.markdown(article.body,
                                     extensions=[
                                         # 包含 縮寫、表格等常用擴展
                                         'markdown.extensions.extra',
                                         # 語法高亮擴展
                                         'markdown.extensions.codehilite',
                                         # 目錄擴展
                                         'markdown.extensions.toc',
                                     ])
...
                                     

添加了一行代碼,接着我們在之前的文章裏添加幾個級別的標題
在這裏插入圖片描述
其實就是插入不同級別的標題,然後在文章任何地方插入**[TOC]**即可自動生成目錄
點擊完成後觀察效果:
在這裏插入圖片描述
如果我們想實現將目錄插入到頁面任何一個位置就徐婭萍修改Markdown的渲染方法:

# 文章詳情
def article_detail(request, id):
    article = ArticlePost.objects.get(id=id)
    article.total_views += 1
    article.save(update_fields=['total_views'])
    md = markdown.Markdown(
        extensions=[
            # 包含 縮寫、表格等常用擴展
            'markdown.extensions.extra',
            # 語法高亮擴展
            'markdown.extensions.codehilite',
            # 目錄擴展
            'markdown.extensions.toc',
        ]
    )
    article.body = md.convert(article.body)
    context = {'article': article, 'toc': md.toc}
    # 載入模板,並返回context對象p
    return render(request, 'article/detail.html', context)

爲了能將toc單獨提取出來,我們先將Markdown類賦值給一個臨時變量md,然後用convert()方法將正文渲染爲html頁面。通過md.toc將目錄傳遞給模板。

修改article/detail.html

...
<!-- 文章詳情 -->
    <div class="container">
        <div class="row">
            <div class="col-9">
                <h1 class="col-12 mt-4">{{ article.title }}</h1>
                <div class="col-12 alert alert-success">作者: {{ article.author }}
                    {% if user == article.author %}
                .<a href="#" onclick="confirm_delete()">刪除文章</a>
                .<a href="{% url "article:article_update" article.id %}">編輯文章</a>
                    {% endif %}
                </div>
                <div>
                    瀏覽量:{{ article.total_views }}
                </div>
                <div class="col-12">
                    <p>{{ article.body|safe }}</p>
                </div>
            </div>
            {# 目錄 #}
            <div class="col-3 mt-4">
                <h4>
                    <strong>目錄</strong>
                    <hr>
                    <div>
                        {{ toc|safe }}
                    </div>
                </h4>
            </div>
        </div>
    </div>
    <script>
        //刪除文章的函數
        function confirm_delete() {
        ...

將原來的內容裝進col-9的容器中,將右側col-3的空間留給目錄,toc需要|safe標籤才能正確渲染
我們重新刷新頁面:
在這裏插入圖片描述
發現右邊已經正確顯示目錄了

Django個人博客搭建1-創建Django項目和第一個App
Django個人博客搭建2-編寫文章Model模型,View視圖
Django個人博客搭建3-創建superuser並向數據庫中添加數據並改寫視圖
Django個人博客搭建4-配置使用 Bootstrap 4 改寫模板文件
Django個人博客搭建5-編寫文章詳情頁面並支持markdown語法
Django個人博客搭建6-對文章進行增刪查改
Django個人博客搭建7-對用戶登陸註冊等需求的實現
Django個人博客搭建8-優化文章模塊
Django個人博客搭建9-增加文章評論模塊

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章