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']
※要點擊上傳到伺服器,才能取得網址
留言列表