close

Python Django 學習紀錄:架設個人部落格(二)

分頁、統計文章、富文本編輯  

六.、分頁和 shell 命令行模式

(1)分頁功能的用途

部落格文章數量較多的時候,全部加載速度會十分緩慢

而採用分頁加載能夠有效改善

 

(2)shell 命令行模式添加部落格文章

使用shell的方式,有助於熟悉對於django的操作

在專案虛擬環境輸入 python manage.py shell 進入 shell 命令行模式

接著導入會使用到的資料庫模組

就可以透過 shell 命令行模式來新增部落格文章了

以下為範例程式:

6.JPG

可以 runserver 來看看成果

7.JPG

緊接著使用 for 迴圈 一次新增多篇文章,供之後測試分頁使用

8.JPG

(3)分頁器實現分頁

1.分頁器介紹

使用 Django 本身提供的分頁器 Paginator

請在<views.py>的上方加入以下代碼導入:

from django.core.paginator import Paginator

分頁器的使用方式:

paginator = Paginator(object_list(資料庫列表), each_page_count(每頁資料數量))

頁面呼叫方式:

page1 = paginator.page(1) 

 

2.設定模型默認排序

使用分頁器的時候需要有排序的準則,或沒有設置的話系統會跳出提醒

所以打開 <models.py> 來對 Blog 資料表添加默認排序的設置

以下為 <models.py> 更新後的代碼:

from django.db import models
from django.contrib.auth.models import User #導入內建的使用者模組
# Create your models here.

class BlogType(models.Model): #部落格文章類型
    type_name = models.CharField(max_length=15)

    def __str__(self):
        return self.type_name

class Blog(models.Model): #部落格文章(由於會用外鍵連接到部落格文章類型,所以此模型要放在後面)
    title = models.CharField(max_length=50)
    content = models.TextField()
    author = models.ForeignKey(User, on_delete=models.CASCADE) #使用外鍵連接到使用者模組,由於尚未建立先不做任何事情
    created_time = models.DateTimeField(auto_now_add = True)
    last_updated_time = models.DateTimeField(auto_now = True)
    blog_type = models.ForeignKey(BlogType, on_delete=models.CASCADE)  #使用外鍵連接到部落格文章類型

    def __str__(self):
        return "<Blog: %s>" % self.title

    class Meta:
        ordering = ['-created_time']

設定好後要進行makemigrations 和 migrate 並重新 runserver

9.JPG

(4)分頁的使用

前端:發送請求,請求打開具體分頁內容

後端:處理請求,返回具請分頁內容響應請求

這邊採用 GET 的方法取得分頁參數,而非之前採用Django傳送參數的方法

首先在<views.py>中,修改blog_list的內容,將分頁器加入其中: 

from django.shortcuts import render, render_to_response, get_object_or_404
from django.core.paginator import Paginator #導入分頁器
from .models import Blog, BlogType

def blog_list(request):

    blogs_all_list = Blog.objects.all()
    paginator = Paginator(blogs_all_list, 5) #每5篇進行分頁
    page_num = request.GET.get('page', 1) #獲取url的頁面參數 (GET請求)
    page_of_blogs = paginator.get_page(page_num) #get_page會自動識別頁碼,若無效則返回1,超出頁數則顯示最後一頁

    context={} #建立字典
    context['blogs'] = page_of_blogs.object_list
    context['page_of_blogs'] = page_of_blogs
    context['blog_types'] = BlogType.objects.all() #將資料放入字典
    return render(request, 'blog/blog_list.html', context) #將字典返回到'bloglist.html'

修改<blog_list.html>,在文章內容後加入分頁: 

                    <!-- 分頁 -->
                    <div>
                         <ul class="pagination">
                            <!-- 上一頁 -->
                            <li>
                                {% if page_of_blogs.has_previous %}
                                  <a href="?page={{ page_of_blogs.previous_page_number }}" aria-label="Previous">
                                    <span aria-hidden="true">&laquo;</span>
                                  </a>
                                {% else %}
                                    <span aria-hidden="true">&laquo;</span>
                                {% endif %}
                            </li>
                            <!-- 全部頁碼 -->
                            {% for page_num in page_of_blogs.paginator.page_range %}
                                <li><a href="?page={{ page_num }}">{{ page_num }}</a></li>
                            {% endfor %}
                            <!-- 下一頁 -->
                            <li>
                                {% if page_of_blogs.has_next %}
                                  <a href="?page={{ page_of_blogs.next_page_number }}" aria-label="Next">
                                    <span aria-hidden="true">&raquo;</span>
                                  </a>
                                {% else %}
                                    <span aria-hidden="true">&raquo;</span>
                                {% endif %}
                            </li>
                          </ul>
                    </div>

(5)settings 自定義參數

公用的全局設置可以放在settings中來統一管理

在文件開頭添加以下代碼: 

from django.conf import settings

而這邊我們在settings文件的最下方加入以下代碼,為每個分頁的文章篇數,供之後使用: 

#自定義參數
EACH_PAGE_BLOGS_NUMBER = 2

(6)分頁的優化顯示

友好的用戶體驗:凸顯當前頁碼、不要過多的頁碼選擇(影響頁面布局)

1.縮減頁碼與加入省略符號

這邊有些對於初學者可能會有些複雜,我將詳細的註解都打在程式碼之中

打開<views.py>,並在 blog_list 中加入關於分頁器的代碼:   

def blog_list(request):

    blogs_all_list = Blog.objects.all()
    paginator = Paginator(blogs_all_list, settings.EACH_PAGE_BLOGS_NUMBER) #根據settings的自定義參數進行分頁
    page_num = request.GET.get('page', 1) #獲取url的頁面參數 (GET請求)
    page_of_blogs = paginator.get_page(page_num) #get_page會自動識別頁碼,若無效則返回1,超出頁數則顯示最後一頁
    current_page_num = page_of_blogs.number #獲取當前頁碼
    #獲取當前頁碼前後各2頁範圍
        #list 建立串列、range 範圍內的所有值(不包含最後一項,所以尾項要+1)、max 取最大值、min 取最小值、paginator.num_pages 取得總頁數
        #串列(範圍(當前頁數-2,到當前頁數,再跟1相比,小於1則取1)
        #    +範圍(當前頁數,到當前頁數+2,再跟總頁數相比,若大於總頁數則取總頁數)尾項加上1)
    page_range = list(range(max(1, current_page_num - 2), current_page_num)) + \
                 list(range(current_page_num, min(paginator.num_pages, current_page_num + 2) + 1))
    #加上省略頁碼標記
    if page_range[0] -1 >= 2:  #若第0項減1小於等於2
        page_range.insert(0, '...') #則在串列插入省略符號
    if page_range[-1] + 2 <= paginator.num_pages: #若最後一項加2大於等於總頁數
        page_range.append('...') #則在串列最後加上省略符號
    #加上首頁與尾頁
    if page_range[0] != 1: #若串列第0項不為1
        page_range.insert(0, 1) #則在第0項插入1
    if page_range[-1] != paginator.num_pages: #若串列最後一項項不為總頁數
        page_range.append(paginator.num_pages) #則在串列最後加上總頁數



    context={} #建立字典
    context['blogs'] = page_of_blogs.object_list
    context['page_of_blogs'] = page_of_blogs
    context['blog_types'] = BlogType.objects.all() #將資料放入字典
    context['page_range'] = page_range #取得分頁器資訊
    return render(request, 'blog/blog_list.html', context) #將字典返回到'bloglist.html'

這裡先完成了後端,接著前端與凸顯代碼一起編寫

2.凸顯當前頁碼與 刪除省略符號的超連結

使用 if 條件判斷

在使用者到該分頁的時候,頁碼將呈現active狀態

而如果頁碼為省略符號,也使用 if 條件判斷 顯示為無超連結的頁碼

在 <blog_list.html> 中的<ul class="pagination"> 修改為以下代碼:   

                         <ul class="pagination">
                            <!-- 上一頁 -->
                            <li>
                                {% if page_of_blogs.has_previous %}
                                  <a href="?page={{ page_of_blogs.previous_page_number }}" aria-label="Previous">
                                    <span aria-hidden="true">&laquo;</span>
                                  </a>
                                {% else %}
                                    <span aria-hidden="true">&laquo;</span>
                                {% endif %}
                            </li>
                            <!-- 全部頁碼 -->
                            {% for page_num in page_range %}
                                <!-- 若為當前頁碼,則加入active狀態 -->
                                {% if page_num == page_of_blogs.number %}
                                    <li class="active"><span>{{ page_num }}</span></li>
                                {% else %}
                                    <!-- 若為省略號,則沒有超連結 -->
                                    {% if page_num == '...' %}
                                        <li ><span>{{ page_num }}</span></li>
                                    {% else %}
                                        <li ><a href="?page={{ page_num }}">{{ page_num }}</a></li>
                                    {% endif %}
                                {% endif %}
                            {% endfor %}
                            <!-- 下一頁 -->
                            <li>
                                {% if page_of_blogs.has_next %}
                                  <a href="?page={{ page_of_blogs.next_page_number }}" aria-label="Next">
                                    <span aria-hidden="true">&raquo;</span>
                                  </a>
                                {% else %}
                                    <span aria-hidden="true">&raquo;</span>
                                {% endif %}
                            </li>
                          </ul>

下方為目前完成部分的展示:

10.JPG

(7)分類列表的分頁整合

之前添加的分頁器只包含在部落格文章列表

在分類列表還無法生效,所以要打開<views.py> 修改 blogs_with_type 的代碼:   

def blogs_with_type(request, blog_type_pk): #導入 blog_type_pk 變數

    blog_type = get_object_or_404(BlogType, pk=blog_type_pk) #獲取編號
    blogs_all_list = Blog.objects.filter(blog_type=blog_type) #取得分類後的資料
    paginator = Paginator(blogs_all_list, settings.EACH_PAGE_BLOGS_NUMBER) #根據settings自定義參數進行分頁
    page_num = request.GET.get('page', 1) #獲取url的頁面參數 (GET請求)
    page_of_blogs = paginator.get_page(page_num) #get_page會自動識別頁碼,若無效則返回1,超出頁數則顯示最後一頁
    current_page_num = page_of_blogs.number #獲取當前頁碼
    #獲取當前頁碼前後各2頁範圍
        #list 建立串列、range 範圍內的所有值(不包含最後一項,所以尾項要+1)、max 取最大值、min 取最小值、paginator.num_pages 取得總頁數
        #串列(範圍(當前頁數-2,到當前頁數,再跟1相比,小於1則取1)
        #    +範圍(當前頁數,到當前頁數+2,再跟總頁數相比,若大於總頁數則取總頁數)尾項加上1)
    page_range = list(range(max(1, current_page_num - 2), current_page_num)) + \
                 list(range(current_page_num, min(paginator.num_pages, current_page_num + 2) + 1))
    #加上省略頁碼標記
    if page_range[0] -1 >= 2:  #若第0項減1小於等於2
        page_range.insert(0, '...') #則在串列插入省略符號
    if page_range[-1] + 2 <= paginator.num_pages: #若最後一項加2大於等於總頁數
        page_range.append('...') #則在串列最後加上省略符號
    #加上首頁與尾頁
    if page_range[0] != 1: #若串列第0項不為1
        page_range.insert(0, 1) #則在第0項插入1
    if page_range[-1] != paginator.num_pages: #若串列最後一項項不為總頁數
        page_range.append(paginator.num_pages) #則在串列最後加上總頁數



    context={} #建立字典
    context['blogs'] = page_of_blogs.object_list #取得當前頁碼的資料
    context['page_of_blogs'] = page_of_blogs
    context['page_range'] = page_range #取得分頁器資訊
    context['blog_type'] = blog_type #將過濾後的資料存入變數
    context['blog_types'] = BlogType.objects.all() #將資料放入字典(繼承blog_list需要用到相同的變數)
    return render(request, 'blog/blogs_with_type.html', context)

修改<blog_with_type.html>

由於文章數量會繼承<blog_list.html>分頁器下方的文章數量訊息,所以這邊可以將原先的訊息刪除:   

<!-- 繼承模板 -->
{% extends "blog/blog_list.html" %}
{% load staticfiles %} <!-- 加載靜態文件 -->
<!-- 繼承標題區塊 -->
{% block title %}
{{ blog_type.type_name }}
{% endblock %}

<!-- 繼承內容區塊 -->
{% block blog_list_title %}
    分類:{{ blog_type.type_name }} 
    <a href="{% url 'blog_list' %}">查看全部部落格文章</a>
{% endblock %}

 

下方為目前完成部分的展示:

11.JPG

六.、上下篇文章按月分類

(1)filter 篩選條件(等於)

大於:__gt (greater than)

大於等於:__gte

小於:__lt (less than)

小於等於:__lte

包含:__contains  (加icontain忽略大小寫)

開頭是:__startswith

結尾是:__endswith

其中之一:__in

範圍:__range

(2)exclude 排除條件(不等於)

為filter的條件取反


(3)條件中的雙下畫線

字段查詢類型

外鍵拓展(以部落格分類為例)

日期拓展(以月分分類為例)

支持鏈式查詢:可以一直鏈接下去

 

(4)上一篇和下一篇部落格文章

在文章詳細頁面<blog_detail.html> 添加 上一篇、下一篇的文章標題與連結

在<views.py>中的 blog_detail 函數中修改以下代碼:   

def blog_detail(request, blog_pk): #導入 blog_pk 變數
    context={} #建立字典
    blog = get_object_or_404(Blog, pk=blog_pk)  #獲取blog_pk
    
    #資料庫文章日期已經為由新到舊排列(新文章日期大於舊文章日期)
    #獲取前一篇文章,取得大於該文章的日期中的最後一篇
    context['previous_blog'] = Blog.objects.filter(created_time__gt=blog.created_time).last()
    #獲取後一篇文章,取得小於該文章的日期中的第一篇
    context['next_blog'] = Blog.objects.filter(created_time__lt=blog.created_time).first()
    context['blog'] = blog
    return render(request, 'blog/blog_detail.html', context) #將字典返回到'blog_detail.html'

接著在<blog_detail.html>下方添加上一篇、下一篇的文章標題與連結

若已經無文章,則顯示"沒有了",代碼如下˙:   

<!-- 繼承模板 -->
{% extends "base.html" %}

<!-- 繼承標題區塊 -->
{% block title %}{{ blog.title }}{% endblock %}

{% block nav_blog_active %}active{% endblock %}

{% load staticfiles %} <!-- 加載靜態文件 -->
{% block header_extends %}
<link rel="stylesheet" href="{% static 'css/blog.css' %}">
{% endblock %}  <!-- 用於擴展額外 css -->
<!-- 繼承內容區塊 -->
{% block content %}
    <div class="container">
        <div class="row">
            <div class="col-xs-10 col-xs-offset-1">
                <h3>{{ blog.title }}</h3>
                <ul class="blog-info-description">
                    <li>作者:{{ blog.author }}</li>
                    <li>分類:<a href="{% url 'blogs_with_type' blog.blog_type.pk %}">{{ blog.blog_type }}</a></li>
                    <!-- 將日期改為純數字24時制顯示 -->
                    <li>發表日期:{{ blog.created_time|date:"Y-m-d H:i:s" }}</li>
                </ul>
                <div class="blog-content">{{ blog.content }}</div>
                <!-- 上一篇、下一篇文章標題連結 -->
                <div class="blog_more">
                    <p>上一篇:
                        {% if previous_blog %}
                            <a href="{% url 'blog_detail' previous_blog.pk %}">{{ previous_blog.title }}</a></p>
                        {% else %}
                            沒有了
                        {% endif %}
                    <p>下一篇:{% if next_blog %}
                            <a href="{% url 'blog_detail' next_blog.pk %}">{{ next_blog.title }}</a></p>
                        {% else %}
                            沒有了
                        {% endif %}
                    </p>
                </div>
            </div>
        </div>
    </div>


{% endblock %}

(5)按月分類

1.url路徑設定

首先在blog專案的<urls.py>添加新的路徑:   

from django.urls import path
from . import views

urlpatterns = [
    path('',views.blog_list, name='blog_list'), #將原先的部落格頁面路徑修改至此
    #連接文章標題至文章詳細頁面
    path('<int:blog_pk>', views.blog_detail, name = "blog_detail"),
    #連接文章份類至分類頁面
    path('type/<int:blog_type_pk>', views.blogs_with_type, name = "blogs_with_type"),
    path('date/<int:year>/<int:month>', views.blogs_with_date, name = "blogs_with_date"),

]

2.後端編寫

接著在blog目錄中的<views.py>中,新建一個名為blogs_with_date 的函數

由於與blogs_with_type 函數大部分內容相同,這邊只在有修改的部分加上註解:   

def blogs_with_date(request, year, month): 

    blogs_all_list = Blog.objects.filter(created_time__year = year, created_time__month = month) #篩選取得日期歸檔後的資料
    
    paginator = Paginator(blogs_all_list, settings.EACH_PAGE_BLOGS_NUMBER) 
    page_num = request.GET.get('page', 1) 
    page_of_blogs = paginator.get_page(page_num)
    current_page_num = page_of_blogs.number 
    page_range = list(range(max(1, current_page_num - 2), current_page_num)) + \
                 list(range(current_page_num, min(paginator.num_pages, current_page_num + 2) + 1))
    if page_range[0] -1 >= 2: 
        page_range.insert(0, '...') 
    if page_range[-1] + 2 <= paginator.num_pages: 
        page_range.append('...') 
    if page_range[0] != 1: 
        page_range.insert(0, 1) 
    if page_range[-1] != paginator.num_pages: 
        page_range.append(paginator.num_pages) 



    context={} 
    #供面板標題使用(傳送到<blogs_with_date.html>模板頁面)
    context['blogs_with_date'] = '%s年%s月' % (year, month) 
    context['blog_dates'] = Blog.objects.dates('created_time','month',order='DESC')#降序排序日期

    context['blogs'] = page_of_blogs.object_list 
    context['page_of_blogs'] = page_of_blogs
    context['page_range'] = page_range 
    context['blog_types'] = BlogType.objects.all() 
    return render(request, 'blog/blogs_with_date.html', context) #傳送到<blogs_with_date.html>模板頁面

在blog_list 函數的字典也要加上日期的變數:   

context['blog_dates'] = Blog.objects.dates('created_time','month',order='DESC')#降序排序日期

3.前端文章列表添加"日期歸檔"面板、創建日期歸檔模板

打開<blog_list.html>在分類面板下面新增日期歸檔面板:   

                    <div class="panel panel-default">
                        <div class="panel-heading">日期歸檔</div>
                        <div class="panel-body">
                            <ul class="blog_date">
                                {% for blog_date in blog_dates %}
                                    <li>
                                        <a href="{% url 'blogs_with_date' blog_date.year blog_date.month %}">{{ blog_date|date:"Y年m月" }}</a>
                                    </li>
                                {% endfor %}
                            </ul>
                        </div>
                    </div>

在blog目錄創建一個名為<blog_with_date.html>的模板

內容與<blog_with_type.html>大部分相同,只有面板標題需要做更改:   

<!-- 繼承模板 -->
{% extends "blog/blog_list.html" %}
{% load staticfiles %} <!-- 加載靜態文件 -->
<!-- 繼承標題區塊 -->
{% block title %}
{{ blog_type.type_name }}
{% endblock %}

<!-- 繼承內容區塊 -->
{% block blog_list_title %}
    日期歸檔:{{ blogs_with_date }} 
    <a href="{% url 'blog_list' %}">查看全部部落格文章</a>
{% endblock %}

 

(6)重複程式碼精簡:

這邊將blog目錄的<views.py>中,多個函數共同使用的相同代碼獨立出來

新定義一個名為 get_blog_list_common_data 的函數,並將會共同使用的代碼寫入其中

最後返回字典,供其他函數使用:   

def get_blog_list_common_data(request, blogs_all_list):

    paginator = Paginator(blogs_all_list, settings.EACH_PAGE_BLOGS_NUMBER) #根據settings自定義參數進行分頁
    page_num = request.GET.get('page', 1) #獲取url的頁面參數 (GET請求)
    page_of_blogs = paginator.get_page(page_num) #get_page會自動識別頁碼,若無效則返回1,超出頁數則顯示最後一頁
    current_page_num = page_of_blogs.number #獲取當前頁碼
    #獲取當前頁碼前後各2頁範圍
        #list 建立串列、range 範圍內的所有值(不包含最後一項,所以尾項要+1)、max 取最大值、min 取最小值、paginator.num_pages 取得總頁數
        #串列(範圍(當前頁數-2,到當前頁數,再跟1相比,小於1則取1)
        #    +範圍(當前頁數,到當前頁數+2,再跟總頁數相比,若大於總頁數則取總頁數)尾項加上1)
    page_range = list(range(max(1, current_page_num - 2), current_page_num)) + \
                 list(range(current_page_num, min(paginator.num_pages, current_page_num + 2) + 1))
    #加上省略頁碼標記
    if page_range[0] -1 >= 2:  #若第0項減1小於等於2
        page_range.insert(0, '...') #則在串列插入省略符號
    if page_range[-1] + 2 <= paginator.num_pages: #若最後一項加2大於等於總頁數
        page_range.append('...') #則在串列最後加上省略符號
    #加上首頁與尾頁
    if page_range[0] != 1: #若串列第0項不為1
        page_range.insert(0, 1) #則在第0項插入1
    if page_range[-1] != paginator.num_pages: #若串列最後一項項不為總頁數
        page_range.append(paginator.num_pages) #則在串列最後加上總頁數

    context={} #建立字典
    context['blogs'] = page_of_blogs.object_list
    context['page_of_blogs'] = page_of_blogs
    context['blog_types'] = BlogType.objects.all() #將資料放入字典
    context['blog_dates'] = Blog.objects.dates('created_time','month',order='DESC')
    context['page_range'] = page_range #取得分頁器資訊

    return context

而其他三個函數修改如下:   

def blog_list(request):

    blogs_all_list = Blog.objects.all()

    context= get_blog_list_common_data(request, blogs_all_list)

    return render(request, 'blog/blog_list.html', context) #將字典返回到'bloglist.html'

def blogs_with_type(request, blog_type_pk): #導入 blog_type_pk 變數

    blog_type = get_object_or_404(BlogType, pk=blog_type_pk) #獲取編號
    blogs_all_list = Blog.objects.filter(blog_type=blog_type) #取得分類後的資料

    context = get_blog_list_common_data(request, blogs_all_list)
    context['blog_type'] = blog_type #將過濾後的資料存入變數

    return render(request, 'blog/blogs_with_type.html', context)

def blogs_with_date(request, year, month): 

    blogs_all_list = Blog.objects.filter(created_time__year = year, created_time__month = month) #篩選取得日期歸檔後的資料    

    context = get_blog_list_common_data(request, blogs_all_list)
    #供面板標題使用(傳送到<blogs_with_date.html>模板頁面)
    context['blogs_with_date'] = '%s年%s月' % (year, month) 

    return render(request, 'blog/blogs_with_date.html', context) #傳送到<blogs_with_date.html>模板頁面

 

七.、文章統計

在文章分類與日期歸檔的地方,添加統計文章數量的部分

最後執行結果如圖:

12.JPG

 1.文章分類統計: 

使用 annotate 註釋,拓展查詢字段

在 <views.py> get_blog_list_common_data 函數中,修改查詢分類的字典,將文章數量也加入字典中

首先導入計算資料庫數量的模組:   

from django.db.models import Count

接著建立一個名為 blog_type_count 的變數,來放入查詢文章與數量的資料:   

    #獲取分類文章與數量
    blog_type_count = BlogType.objects.annotate(blog_count=Count('blog')) #統計BlogType關聯項的Blog模組的小寫
    context['blog_types'] = blog_type_count

修改<blog_list.html>頁面:   

                <div class="hidden-xs col-sm-4 col-md-3 col-lg-2">
                    <div class="panel panel-default">
                        <div class="panel-heading">部落格分類</div>
                        <div class="panel-body">
                            <ul class="blog-types">
                            {% for blog_type in blog_types %}
                                <li><a href="{% url 'blogs_with_type' blog_type.pk %}">{{ blog_type.type_name }}({{ blog_type.blog_count }})</a></li>
                            {% empty %}
                                <li>暫無分類,敬請期待</li>
                            {% endfor %}
                            </ul>
                        </div>
                    </div>

 2.日期歸檔統計: 

使用字典的方式來查詢,鍵為日期歸檔的資料,值為篇數

在 <views.py> get_blog_list_common_data 函數中,修改查詢日期歸檔的字典,將文章數量也加入字典中:   

    #獲取日期歸檔與數量
    blog_dates = Blog.objects.dates('created_time','month',order='DESC') #日期歸檔資料
    blog_dates_dict={} #建立字典(鍵(日期歸檔的資料):值(篇數))
    for blog_date in blog_dates:
        blog_count = Blog.objects.filter(created_time__year=blog_date.year,
                                         created_time__month=blog_date.month).count()
        blog_dates_dict[blog_date] = blog_count #迴圈儲存"鍵 = [值]"

修改<blog_list.html>頁面:   

                    <div class="panel panel-default">
                        <div class="panel-heading">日期歸檔</div>
                        <div class="panel-body">
                            <ul class="blog_date">
                                {% for blog_date, blog_count in blog_dates.items %}
                                    <li>
                                        <a href="{% url 'blogs_with_date' blog_date.year blog_date.month %}">{{ blog_date|date:"Y年m月" }}({{ blog_count }})</a>
                                    </li>
                                {% endfor %}
                            </ul>
                        </div>
                    </div>

八.、部落格後台富文本編輯與上傳圖片

 1.使用django-ckeditor: 

django有許多的編輯器,選擇 django-ckeditor 的標準如下:

  • 具有基本的富文本編輯功能
  • 可以上傳圖片
  • 可以查看原碼
  • 有持續更新   

django-ckeditor 的相關文件點此

 2.安裝django-ckeditor: 

(1)安裝

在虛擬環境中的 mysite 專案安裝套件,指令如下:   

pip install django-ckeditor

(2)註冊應用

在 settings 的應用中,添加以下代碼:   

'ckeditor',

(3)配置 model

導入套件,並將文章內文的字段改為RichTextField:   

from django.db import models
from django.contrib.auth.models import User #導入內建的使用者模組
from ckeditor.fields import RichTextField #導入富文本套件
# Create your models here.

class BlogType(models.Model): #部落格文章類型
    type_name = models.CharField(max_length=15)

    def __str__(self):
        return self.type_name

class Blog(models.Model): #部落格文章(由於會用外鍵連接到部落格文章類型,所以此模型要放在後面)
    title = models.CharField(max_length=50)
    content = RichTextField() #改為RichTextField
    author = models.ForeignKey(User, on_delete=models.CASCADE) #使用外鍵連接到使用者模組,由於尚未建立先不做任何事情
    created_time = models.DateTimeField(auto_now_add = True)
    last_updated_time = models.DateTimeField(auto_now = True)
    blog_type = models.ForeignKey(BlogType, on_delete=models.CASCADE)  #使用外鍵連接到部落格文章類型

    def __str__(self):
        return "<Blog: %s>" % self.title

    class Meta:
        ordering = ['-created_time']

(4)解析html

導入富文本編輯後,顯示的文字會連同html代碼一同顯示,所以要將這些代碼屏蔽掉

首先是<blog_detail.html>,只需修改一行代碼:   

                <div class="blog-content">{{ blog.content | safe }}</div>

 

接著是<blog_list.html>,由於此處有省略字符,不適合用safe的方式,所以採用以下代碼:   

                                    <!-- truncatechars:<int> (縮略長文) -->
                                    <p>{%autoescape off%} {{blog.content|truncatechars:120}} {%endautoescape%}</p>

 

 

3.添加上傳圖片的功能: 

(1)安裝

在虛擬環境中的 mysite 專案安裝套件,指令如下:   

pip install pillow

(2)註冊應用

在 settings 的應用中,添加以下代碼:   

'ckeditor_uploader',

(3)配置settings

在 settings 的最下方自定義參數前,添加以下代碼: :   

# media
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR,'media')

#配置ckeditor
CKEDITOR_UPLOAD_PATH = 'upload/'

(4)配置url

開發的時候使用下列方法添加url,部屬的時候需要用其他的方法:   

from django.contrib import admin
from django.urls import path, include
from . import views
from django.conf import settings #導入settings
from django.conf.urls.static import static #導入static

urlpatterns = [
    path('', views.home, name = 'home'), #新增的首頁路徑
    path('admin/', admin.site.urls),
    path('blog/', include('blog.urls')), #導入應用中設置的路徑
    path('ckeditor',include('ckeditor_uploader.urls')), #引入路徑
]

urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) #導入路徑

(5)配置model把字段改成RichTextUploadingField

將<models.py>的代碼修改如下:   

from django.db import models
from django.contrib.auth.models import User #導入內建的使用者模組
from ckeditor_uploader.fields import RichTextUploadingField #改為從uploader導入 RichTextUploadingField
# Create your models here.

class BlogType(models.Model): #部落格文章類型
    type_name = models.CharField(max_length=15)

    def __str__(self):
        return self.type_name

class Blog(models.Model): #部落格文章(由於會用外鍵連接到部落格文章類型,所以此模型要放在後面)
    title = models.CharField(max_length=50)
    content = RichTextUploadingField() #修改此處
    author = models.ForeignKey(User, on_delete=models.CASCADE) #使用外鍵連接到使用者模組,由於尚未建立先不做任何事情
    created_time = models.DateTimeField(auto_now_add = True)
    last_updated_time = models.DateTimeField(auto_now = True)
    blog_type = models.ForeignKey(BlogType, on_delete=models.CASCADE)  #使用外鍵連接到部落格文章類型

    def __str__(self):
        return "<Blog: %s>" % self.title

    class Meta:
        ordering = ['-created_time']

13.JPG

※要點擊上傳到伺服器,才能取得網址

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 ivankao 的頭像
    ivankao

    IvanKao的部落格

    ivankao 發表在 痞客邦 留言(0) 人氣()