Python Django 學習紀錄:架設個人部落格(一)
構建、美化
在網路上尋到 杨仕航 的網站
有許多 Django 相關的技術文章,也有關於 Django 的教學視頻
這是他的 billbill空間 再敲一行代码
在這裡寫下我從他的視頻學習到的技術
並將如何架設個人部落格的重點與過程,精簡的記錄下來
一.、構建個人部落格
1.簡單構建:
站功能模塊:(Django App)
- 部落格
- 文章
- 分類
- 標籤
- 評論
- 點讚
- 閱讀
- 用戶 - 第三方登陸
2.建立虛擬環境:
避免多個專案之間的python互相衝突
可以完整又方便的快速導出 python 套件列表
使用以下指令安裝:
pip install virtualenv
可以使用下列操作指令:
創建虛擬機:
virtualenv <虛擬環境名稱>
啟動虛擬機:
Scripts/activate
退出虛擬機:
deactivate
創建一個名為<mysiteenv>的虛擬機
完成後進入目錄並啟動虛擬機,使用以下指令安裝 Django:
pip install Django
3.初步創建部落格專案與應用:
(1)創建專案與應用
創建一個名為<mysite>的專案:
django-admin startproject mysite
切換進入mysite目錄:
cd mysite
創建一個名為<blog>的應用:
python manage.py startapp blog
這時候目錄會是這個樣子:

(2)建立應用模型
接下來創建部落格文章、部落格分類的資料庫模型:
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
(3)創建超級使用者
先將生成的文件應用到資料庫:
python manage.py migrate
創建超級使用者:
python manage.py createsuperuser
建立好後打開<settings.py>
在INSTALLED_APPS中加入 'blog' 這個剛剛創建的 App
這裡可以參照以前的方法順便將admin系統語系改為中文(zh-hant)
接著創建更改的文件,並將生成的文件應用到資料庫:
python manage.py makamigrations python manage.py migrate
(4)註冊資料庫模型至Django資料庫
為了方便查詢資料庫,先將資料庫模型註冊至Django的 admin系統:
from django.contrib import admin
from .models import BlogType,Blog
# Register your models here.
@admin.register(BlogType)
class BlogTypeAdmin(admin.ModelAdmin):
list_display = ('id', 'type_name')
@admin.register(Blog)
class BlogAdmin(admin.ModelAdmin):
list_display = ('title','blog_type','author','created_time','last_updated_time')
(5)創建分類與文章進行測試
啟動虛擬機,在mysite目錄輸入以下指令:
python manage.py runserver
使用瀏覽器開啟「 "http://127.0.0.1:8000/admin/」
登入剛才創建的超級使用者
並創建一個分類與一篇文章,方便之後能夠測試其他功能
二.、常用的模板標籤和過濾器
1.常用模板標籤
循環:for
條件:if(邏輯判斷)、ifequal、ifnotequal
鏈接:url
模版嵌套:block、extends、include
注釋: {# #}、<!-- -->
日期:date
字數截取:truncatechars、truncatechars_html、truncatewords、truncatewords_html
是否信任html:safe
長度:length
2.建立視圖
打開<views.py>來建立初步的視圖:
from django.shortcuts import render, render_to_response, get_object_or_404
from .models import Blog, BlogType
def blog_list(request):
context={} #建立字典
context['blogs'] = Blog.objects.all() #將資料放入字典
return render(request, 'blog/blog_list.html', context) #將字典返回到'bloglist.html'
def blog_detail(request, blog_pk): #導入 blog_pk 變數
context={} #建立字典
context['blog'] = get_object_or_404(Blog, pk=blog_pk) #獲取blog_pk
return render(request, 'blog/blog_detail.html', context) #將字典返回到'blog_detail.html'
def blogs_with_type(request, blog_type_pk): #導入 blog_type_pk 變數
context={}
blog_type = get_object_or_404(BlogType, pk=blog_type_pk) #獲取編號
context['blogs'] = Blog.objects.filter(blog_type=blog_type) #將編號使用過濾器過濾
context['blog_type'] = blog_type #將過濾後的資料存入變數
return render(request, 'blog/blogs_with_type.html', context)
3.建立模版<templates>
在mysite專案底下建立全局模版<templates>目錄
並開啟<settings.py>設定路徑,在'DIRS':[ ]中添加以下內容:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates'),],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
首先要建立的是基礎模板<base.html>,來讓其他三個模版繼承
(1)建立<base.html>(基礎模版):
<!DOCTYPE html>
<html>
<head><meta charset="utf-8">
<!-- 將標題區塊化 -->
<title>{% block title %}{% endblock %}</title>
</head>
<body>
<div>
<!-- 連接回主頁 -->
<a href="{% url 'home' %}">
<h3>個人部落格</h3>
</a>
</div>
<hr>
{% block content %}{% endblock %}
</body>
</html>
在<templates>目錄底下建立<blog>目錄,專門用來存放blog應用的相關模版
建立三個模板,分別是:主頁面、文章詳細頁面、分類文章頁面
將要繼承<base.html>基礎模版
(2)建立<blog_list.html>(主頁面)文件:
<!-- 繼承模板 -->
{% extends "base.html" %}
<!-- 繼承標題區塊 -->
{% block title %}
我的部落格
{% endblock %}
<!-- 繼承內容區塊 -->
{% block content %}
<!-- 取得 blogs 篇數 -->
<p>一共有 {{ blogs|length }} 篇文章</p>
{% for blog in blogs %}
<!-- 設定超連結,導向文章詳細頁面 -->
<a href="{% url 'blog_detail' blog.pk %}">
<h3>{{ blog.title }}</h3>
</a>
<!-- truncatechars:<int> (縮略長文) -->
<p>{{ blog.content|truncatechars:30}}</p>
<!-- 若無文章,則顯示以下訊息 -->
{% empty %}
<p>暫無文章,敬請期待</p>
{% endfor %}
{% endblock %}
(3)建立<blog_detail.html>(文章詳細頁面)文件:
<!-- 繼承模板 -->
{% extends "base.html" %}
<!-- 繼承標題區塊 -->
{% block title %}
{{ blog.title }}
{% endblock %}
<!-- 繼承內容區塊 -->
{% block content %}
<h3>{{ blog.title }}</h3>
<p>作者:{{ blog.author }}</p>
<!-- 將日期改為純數字24時制顯示 -->
<p>發表日期:{{ blog.created_time|date:"Y-m-d H:i:s" }}</p>
<a href="{% url 'blogs_with_type' blog.blog_type.pk %}">
<p>分類:{{ blog.blog_type }}</p>
</a>
<p>{{ blog.content }}</p>
{% endblock %}
(4)建立<blog_with_type>(分類頁面)文件:
<!-- 繼承模板 -->
{% extends "base.html" %}
<!-- 繼承標題區塊 -->
{% block title %}
<!-- 取得分類名稱 -->
<title>{{ blog_type.type_name }}</title>
{% endblock %}
<!-- 繼承內容區塊 -->
{% block content %}
<!-- 取得分類名稱 -->
<h3>{{ blog_type.type_name }}</h3>
<!-- 取得 blogs 篇數 -->
<p>一共有 {{ blogs|length }} 篇文章</p>
{% for blog in blogs %}
<!-- 設定超連結,導向文章詳細頁面 -->
<a href="{% url 'blog_detail' blog.pk %}">
<h3>{{ blog.title }}</h3>
</a>
<!-- truncatechars:<int> (縮略長文) -->
<p>{{ blog.content|truncatechars:30}}</p>
<!-- 若無文章,則顯示以下訊息 -->
{% empty %}
<p>暫無文章,敬請期待</p>
{% endfor %}
{% endblock %}
4.建立<urls.py>
在blog裡面建立一個<urls.py>文件,用來設置blog這個應用的調度器:
from django.urls import path
from . import views
urlpatterns = [
#連接文章標題至文章詳細頁面
path('<int:blog_pk>', views.blog_detail, name = "blog_detail"),
#連接文章份類至分類頁面
path('type/<int:blog_type_pk>', views.blogs_with_type, name = "blogs_with_type"),
]
修改<mysite/urls.py>,設置部落格頁面與導入blog 應用中的調度器:
from django.contrib import admin
from django.urls import path, include
from blog.views import blog_list
urlpatterns = [
path('', blog_list, name = 'home'),
path('admin/', admin.site.urls),
path('blog', include('blog.urls')), #導入應用中設置的路徑
]
三.、使用CSS美化頁面
1.頁面設計

2.導覽列設計
LOGO網站名稱 + 導覽
┌---------------------------------------------------------------┐
| 網站名稱 首頁 | 部落格 |
└----------------------------------------------┘
3.使用靜態文件
打開<"settings.py">
並在最下方加入以下代碼來指定路徑:
STATICFILES_DIRS=[ os.path.join(BASE_DIR,'static'), ]
接著在 mysite 根目錄創建一個名為 static 的目錄
然後再 static 目錄中創建一個名為 css 的目錄
4.添加首頁與導覽列
首先要新增一個首頁
我們將這個首頁的路徑設置在mysite專案的根目錄
將原先預設一開啟網站會導入部落格頁面的路徑更換至blog目錄的<"urls.py">
並新增首頁路徑至mysite的<"urls.py">:
(1)mysite的<"urls.py">:
from django.contrib import admin
from django.urls import path, include
from . import views
urlpatterns = [
path('', views.home, name = 'home'), #新增的首頁路徑
path('admin/', admin.site.urls),
path('blog/', include('blog.urls')), #導入應用中設置的路徑
]
(2)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"),
]
接著在mysite專案的根目錄添加一個<"views.py">文件
用來寫專案會用到的全局處理方法
(3)在mysite的<"views.py">文件寫入首頁的處理方法:
from django.shortcuts import render, render_to_response
def home(request):
context ={}
return render(request, 'home.html',context)
(4)在<"base.html">基礎模版頁面新增導覽列的連結、加載靜態文件與導入CSS:
這邊添加一個 block ,用來擴展其他頁面額外的 CSS文件
{% load staticfiles %} <!-- 導入 staticfiles -->
<!DOCTYPE html>
<html>
<head><meta charset="utf-8">
<!-- 將標題區塊化 -->
<title>{% block title %}{% endblock %}</title>
<link rel="stylesheet" href="{% static 'css/base.css' %}"> <!-- 導入 css 文件 -->
{% block header_extends %}{% endblock %} <!-- 用於擴展額外 css -->
</head>
<body>
<div class="nav">
<!-- 連接回主頁 -->
<a class="logo" href="{% url 'home' %}">
<h3>個人部落格</h3>
</a>
<!-- 首頁連結 -->
<a href="/">首頁</a>
<!-- 部落格連結 -->
<a href="{% url 'blog_list' %}">部落格</a>
</div>
{% block content %}{% endblock %}
</body>
</html>
(5)在 mysite 專案的 templates 目錄添加新的模版<"home.html">:
這裡要先加載靜態文件的方法
接著導入屬於 home 的 css文件
{% extends "base.html" %}
{% load staticfiles %} <!-- 加載靜態文件 -->
{% block titles %}
我的網站 | 首頁
{% endblock %}
{% block header_extends %} <!-- 導入 css 文件 -->
<link rel="stylesheet" href="{% static 'css/home.css' %}">
{% endblock %}
{% block content %}
<!-- 加入 class 標籤讓 css 文件辨別 -->
<h3 class="home-content">歡迎訪問我的網站!</h3>
{% endblock %}
5.編寫 CSS 文件
這邊需要有一些關於 CSS 的基礎知識
在 mysite 目錄的 static 目錄中的 css 目錄 創建下列兩個檔案
(1)<"base.css">:
* {
margin: 0; /*預設邊界修改為0*/
padding: 0;
}
div.nav {
background-color: #eee;
border-bottom: 1px solid #ccc; /*底部邊線*/
padding: 10px 5px;
}
div.nav a{
text-decoration: none; /*移除下劃線*/
color: #000; /*導覽列文字連結改為黑色*/
padding: 5px 10px; /*邊距*/
}
div.nav a.logo {
display: inline-block; /*縮到行內*/
font-size: 120%;
}
(2)<"home.css">:
h3.home-content {
font-size: 222%;
text-align: center; /*置中*/
position: absolute; /*改為絕對位置*/
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
四.、CSS框架協助前端佈局
(1)如何選擇 CSS 框架
易用性、兼容性、大小、效果、功能
(2)部署 bootstrap
選擇下載bootstrap
然後選擇下載用於生產環境的 bootstrap
在 mysite 專案中的 static 目錄中 創建一個名為 bootstrap 的資料夾
並將下載下來的檔案解壓縮在該資料夾目錄中
再來請下載最新的 jquery 檔案,放在static 目錄中
接著修改 base.html,讓所有網頁都能夠仔入bootstrap的內容
{% load staticfiles %} <!-- 導入 staticfiles -->
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <!-- 對應IE瀏覽器 -->
<meta name="viewport" content="width=device-width, initial-scale=1"> <!-- 適應響應式設計 -->
<!-- 將標題區塊化 -->
<title>{% block title %}{% endblock %}</title>
<link rel="stylesheet" href="{% static 'css/base.css' %}"> <!-- 導入 css 文件 -->
<link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}">
{% block header_extends %}{% endblock %} <!-- 用於擴展額外 css -->
</head>
<body>
<div class="nav">
<!-- 連接回主頁 -->
<a class="logo" href="{% url 'home' %}">
<h3>個人部落格</h3>
</a>
<!-- 首頁連結 -->
<a href="/">首頁</a>
<!-- 部落格連結 -->
<a href="{% url 'blog_list' %}">部落格</a>
</div>
{% block content %}{% endblock %}
<!-- 導入bootstrap和jquery -->
<script type="text/javascript" src="{% static 'bootstrap/js/bootstrap.min.js' %}"></script>
<script type="text/javascript" src="{% static 'static/jquery-1.12.4.min.js' %}"></script>
</body>
</html>
(3)bootstrap 概要
1.布局容器
.container 用於固定寬度並支持響應式的容器
<div class="container">
.container-fluid 用於 100% 寬度 ,佔據全部 viewport 的容器
<div class="container-fluid">
2.柵格系統
這邊較為複雜 可以到 這裡 詳細了解
簡而言之,就是bootstrap 提供了一套行動裝置優先的流式柵格系統
系統會自動分為最多12列,將整個網頁劃分為許多個區塊
(4)使用 bootstrap 導覽列
接下來將之前使用css設計過的導覽列
改為使用 bootstrap 框架 的導覽列
將<base.html>中導覽列的部分修改如下:
將導航條改回bootstrap的響應式導航條
添加 "視窗較小自動改為下拉式選單" 的響應式功能與按鈕
將導航條隨著頁面一同捲動
添加 "active"狀態的block,例如進入部落格分頁的時候,導航條的部落格色調就會變深灰色(active狀態)
{% load staticfiles %} <!-- 導入 staticfiles -->
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <!-- 對應IE瀏覽器 -->
<meta name="viewport" content="width=device-width, initial-scale=1"> <!-- 適應響應式設計 -->
<!-- 將標題區塊化 -->
<title>{% block title %}{% endblock %}</title>
<link rel="stylesheet" href="{% static 'css/base.css' %}"> <!-- 導入 css 文件 -->
<link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}">
{% block header_extends %}{% endblock %} <!-- 用於擴展額外 css -->
</head>
<body>
<!-- bootstrap 響應式導航條 -->
<div class="navbar navbar-default navbar-fixed-top" role="navigation">
<!-- 選用寬度填滿頁面的容器 -->
<div class="container-fluid">
<!-- 導航條主標題 -->
<div class="navbar-header">
<a class="navbar-brand" href="{% url 'home' %}">個人部落格</a>
<!-- 響應式下拉選單 -->
<button class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
</div>
<!-- 導航條列表-->
<div class="collapse navbar-collapse" id="navbar-collapse">
<ul class="nav navbar-nav">
<!-- 選取的列表加入active狀態-->
<li class="{% block nav_home_active %}{% endblock %}">
<a href="{% url 'home' %}">首頁</a>
</li>
<li class="{% block nav_blog_active %}{% endblock %}">
<a href="{% url 'blog_list' %}">部落格</a>
</li>
</ul>
</div>
</div>
</div>
{% block content %}{% endblock %}
<!-- 導入jquery和bootstrap(jquery必須在前) -->
<script type="text/javascript" src="{% static 'jquery-3.3.1.min.js' %}"></script>
<script type="text/javascript" src="{% static 'bootstrap/js/bootstrap.min.js' %}"></script>
</body>
</html>
在<home.html>添加active塊
{% block nav_home_active %}active{% endblock %}
在<blog_detail.html>、<blog_list.html>、<blogs_with_type.html>添加active塊
{% block nav_blog_active %}active{% endblock %}
最後修改CSS樣式,將原本設定給導航條的樣式刪除,並加入避免導航條蓋住文章內容的代碼( !important 為優先採用)
<"base.css">:
* {
margin: 0; /*預設邊界修改為0*/
padding: 0;
}
body {
margin-top: 80px!important;
}
五.、Bootstrap響應式布局
(1)柵格區塊布局
首先將部落格文章列表<blog_list.html>分成兩大區塊
左半邊佔 8 格,為文章列表與內容
右半邊佔 4 格,為文章分類
為了在該頁面使用文章分類的 models
所以在 blog 應用的<views.py>中的 blog_list 函式中要在字典中加入 BlogType 這個model 的資料:
def blog_list(request):
context={} #建立字典
context['blogs'] = Blog.objects.all() #將資料放入字典
context['blog_types'] = BlogType.objects.all() #將資料放入字典
return render(request, 'blog/blog_list.html', context) #將字典返回到'bloglist.html'
以下為Bootstrap布局後的 <blog_list.html>:
<!-- 繼承模板 -->
{% extends "base.html" %}
<!-- 繼承標題區塊 -->
{% block title %}
我的部落格
{% endblock %}
{% block nav_blog_active %}active{% endblock %}
<!-- 繼承內容區塊 -->
{% block content %}
<div class="container">
<div class="row">
<!-- 左區塊文章內容 -->
<div class="col-md-8">
<!-- 取得 blogs 篇數 -->
<p>一共有 {{ blogs|length }} 篇文章</p>
{% for blog in blogs %}
<!-- 設定超連結,導向文章詳細頁面 -->
<a href="{% url 'blog_detail' blog.pk %}">
<h3>{{ blog.title }}</h3>
</a>
<!-- truncatechars:<int> (縮略長文) -->
<p>{{ blog.content|truncatechars:30}}</p>
<!-- 若無文章,則顯示以下訊息 -->
{% empty %}
<p>暫無文章,敬請期待</p>
{% endfor %}
</div>
<!-- 右區塊文章分類 -->
<div class="col-md-4">
<h4>部落格分類</h4>
{% for blog_type in blog_types %}
<ul><a href="{% url 'blogs_with_type' blog_type.pk %}">{{ blog_type.type_name }}</a></ul>
{% empty %}
<ul>暫無分類,敬請期待</ul>
{% endfor %}
</div>
</div>
</div>
{% endblock %}

(2)應用分工
由於有許多關於 blog 應用的文件
包含< blog_list.html>、<blog_detail.html>、<blog_with_type.html>
還有之後會添加的CSS文件
所以我們將在blog應用中新增一個templates目錄,在其中創建一個名為blog的目錄,並將上面三個模板改放在blog目錄中
並且在blog應用中新增一個名為static的目錄,在其中創建一個名為css的目錄,並在裡面添加一個<blog.css>空白文件供之後修改樣式
(3)添加面板
由於版面還不夠美觀,所以接下來加入面板
可以參考 bootstrap 中文文檔
bootstrap框架後,可能還有些細微調整還是得由CSS修改
所以在此頁面導入在 blog/static/css 目錄的 <blog.css> 文件
接下來則在將兩個區塊分別加上面板
並且對於不同螢幕大小進行響應式設計 :
<!-- 繼承模板 -->
{% extends "base.html" %}
{% load staticfiles %} <!-- 加載靜態文件 -->
<!-- 繼承標題區塊 -->
{% block title %}
我的部落格
{% endblock %}
{% block header_extends %}<link rel="stylesheet" href="{% static 'css/blog.css' %}">{% endblock %} <!-- 用於擴展額外 css -->
{% block nav_blog_active %}active{% endblock %}
<!-- 繼承內容區塊 -->
{% block content %}
<div class="container">
<div class="row">
<!-- 左區塊文章內容 -->
<div class="col-xs-12 col-sm-8 col-md-9 col-lg-10">
<div class="panel panel-default">
<!-- 取得 blogs 篇數、加上面板標題的block 供其他模板使用 -->
<div class="panel-heading">{% block blog_list_title %}部落格文章列表 (一共有 {{ blogs|length }} 篇文章){% endblock %}</div>
<div class="panel-body">
{% for blog in blogs %}
<!-- 命名class,讓css文件修改此區塊樣式 -->
<div class="blog">
<!-- 設定超連結,導向文章詳細頁面 -->
<h3><a href="{% url 'blog_detail' blog.pk %}">{{ blog.title }}</a></h3>
<p class="blog-info">
<span class="glyphicon glyphicon-tag"></span>分類:<a href="{% url 'blogs_with_type' blog.blog_type.pk %}">{{ blog.blog_type }}</a>
<span class="glyphicon glyphicon-time"></span>發表日期:{{ blog.created_time|date:"Y-m-d" }}
</p>
<!-- truncatechars:<int> (縮略長文) -->
<p>{{ blog.content|truncatechars:120}}</p>
</div>
<!-- 若無文章,則顯示以下訊息 -->
{% empty %}
<div class="blog">
<p>暫無文章,敬請期待</p>
</div>
{% endfor %}
</div>
</div>
</div>
<!-- 右區塊文章分類 -->
<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">
{% for blog_type in blog_types %}
<ul><a href="{% url 'blogs_with_type' blog_type.pk %}">{{ blog_type.type_name }}</a></ul>
{% empty %}
<ul>暫無分類,敬請期待</ul>
{% endfor %}
</div>
</div>
</div>
</div>
</div>
{% endblock %}
做出一些CSS的微調與修改:
div.blog h3{
margin-top: 0.5em;
}
div.blog:not(:last-child) { /*最後一行不執行*/
margin-bottom: 2em;
border-bottom: 1px solid#eee;
padding-bottom: 1em;
}
div.blog p.blog-info {
margin-bottom: 0;
}

(4)再度繼承模板
文章分類頁面使用的程式碼與文章列表頁面大同小異
這邊將直接繼承<blog_list.html>
只有在文章列表面板的標題做一點更動
所以在<blog_list.html>已經給了一個{% block blog_list_title %}{%endblock%}
<blog_with_type.html>將改為以下程式碼:
<!-- 繼承模板 -->
{% extends "blog/blog_list.html" %}
{% load staticfiles %} <!-- 加載靜態文件 -->
<!-- 繼承標題區塊 -->
{% block title %}
{{ blog_type.type_name }}
{% endblock %}
<!-- 繼承內容區塊 -->
{% block blog_list_title %}
分類:{{ blog_type.type_name }}
(一共有 {{ blogs|length }} 篇文章)
<a href="{% url 'blog_list' %}">查看全部部落格文章</a>
{% endblock %}
(5)文章詳細頁面布局
在文章詳細頁面的部分也使用bootstrap柵格區塊的劃分
將所有內容改動到畫面正中
將文章資訊的部分改為<ul>與<li>標籤,
最後分別將文章資訊、文章內容加上class標籤,導入<blog.css>對這兩個class做出一些微調
以下為<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>
</div>
</div>
{% endblock %}
以下為<blog.css>程式碼:
div.blog h3{
margin-top: 0.5em;
}
div.blog:not(:last-child) { /*最後一行不執行*/
margin-bottom: 2em;
border-bottom: 1px solid#eee;
padding-bottom: 1em;
}
div.blog p.blog-info {
margin-bottom: 0;
}
ul.blog-info-description {
list-style: none; /*將標籤符號去除*/
margin-bottom: 1em;
}
ul.blog-info-description li {
display: inline-block; /*修改為行內*/
margin-right: 2em;
}
div.blog-content {
text-indent: 2em; /*開頭縮排*/
}

六.、分頁和 shell 命令行模式
(1)分頁功能的用途
部落格文章數量較多的時候,全部加載速度會十分緩慢
而採用分頁加載能夠有效改善
(2)shell 命令行模式添加部落格文章
使用shell的方式,有助於熟悉對於django的操作
在專案虛擬環境輸入 python manage.py shell 進入 shell 命令行模式
接著導入會使用到的資料庫模組
就可以透過 shell 命令行模式來新增部落格文章了
以下為範例程式:

可以 runserver 來看看成果

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

(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

(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">«</span>
</a>
{% else %}
<span aria-hidden="true">«</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">»</span>
</a>
{% else %}
<span aria-hidden="true">»</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">«</span>
</a>
{% else %}
<span aria-hidden="true">«</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">»</span>
</a>
{% else %}
<span aria-hidden="true">»</span>
{% endif %}
</li>
</ul>
下方為目前完成部分的展示:

(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 %}
下方為目前完成部分的展示:

六.、上下篇文章按月分類
(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>模板頁面
七.、文章統計
在文章分類與日期歸檔的地方,添加統計文章數量的部分
最後執行結果如圖:

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']
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_UPLOADER_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']

※要點擊上傳到伺服器,才能取得網址
請先 登入 以發表留言。