如何使用Django3架設簡單的部落格 — — Django教學 EP.1

什麼是Django?

Django 是一個可以快速寫出網站的python web framework ,Instagram 在創立之初就是使用Django作為他們的框架,而現在國外非常多大型的網站都是使用 Django來做後端開發。

他們的slogan

The Web framework for perfectionists with deadlines

意思是說,是一個可以給完美主義者快速架出網站的工具

他有很多預設的優良功能,包含admin後台、安全性設定、ORM、router、MVC架構等等,這些都只需要我們打少量的程式碼就能輕鬆地做出來。

那接下來就開始我們今天的教學吧!
(過程中如果有程式找不到在哪裡看都可以上完整版github repo

前置準備

Disclaimer: 本篇教學專注在Django上,假設你大概知道怎麼用python,電腦上應該是有裝好python的狀態

本篇屬於比較進階一點的教學,不是在教程式的基礎,也不是在教觀念,而是讓懂一點點python的人可以架設屬於自己的部落格。

首先先創建一個project資料夾,並且進入該資料夾

我們先進到 terminal

  • 在 Mac上用 Command+Space 搜尋 terminal
  • 在 Windows 上 開啟 cmd (並確保有安裝好 python 並在環境變數中的PATH變數加入Python的目錄)

接下來我們要創建專案資料夾:

利用 mkdir (make directory 創建目錄)指令 建立一個新的資料夾 django-blog-demo,也可以直接在電腦上新增一個資料夾

mkdir django-blog-demo
cd django-blog-demo

pyenv + pipenv

pyenv是用來管理python版本的工具,可以讓一台電腦上有多個不同版本的python,這在做一個專案中是非常重要的,因為我們不希望我們的python版本不清不楚,造成之後維護,或是檔案共享的困難

pipenv則是 python pip 這個 package manger 比較新的虛擬環境工具, 很多人可能用過 virtualenv, conda, 或是 venv,但我個人比較喜歡 pipenv, 因為 pipenv 可以產生 Pipfile.lock 跟 Pipfile 的檔案。

如果寫過nodejs的人可能就知道,這就很像npm 的 package.json 跟 packge-lock.json 或 yarn-lock.json,可以確保以後重新安裝、或要做部署(發佈到AWS或Digital Ocean等虛擬機)之後(可能放到github上面再git pull下來)可以快速安裝正確版本的套件,避免相容性的問題。

回到剛剛的 “django-blog-demo”

此時我們要在這邊檢查我們 python 的版本。

我們這邊要使用 python 3.7的版本,先使用 pyenv install –list 檢查可安裝的版本, 後面加上 ‘| grep 3.7’ 用來過濾出只含有 ‘3.7’ 這個字串的那幾行

pyenv install --list | grep 3.7

這時候我看到有一個 3.7.6 感覺不錯,就來裝一下(通常一樣是3.7會建議裝後面數字大一點的版本,會功能上比較穩定,也會有很多安全性的修復,如果你的專案之後要放在網路上,這就非常重要)

pyenv install 3.7.6

這邊安裝通常會比較久一點,我們就讓他跑一下

pyenv versions

這時可以看到我們本機上可以切換的python版本,裡面就包含了剛才的 3.7.6

接下來確保我們在剛剛的 ‘django-blog-demo’中,可以使用 pwd 這個指令檢查現在的位子

pwd

此時顯示出來,後面應該要是django-demo-blog或是你設定的資料夾名稱。沒問題後我們就可以開始設定環境

pyenv local 3.7.6

這個指令是用來設定現在這個資料夾裡面的python版本,我們把它設成3.7.6

接下來要來安裝django(到這個目錄裡)

有別於一般習慣的 pip install, 我們要用 pipenv install,這樣他只會安裝到我們這個目錄中,並且可以做好版本管理。(類似nodejs的 npm install)

我們先確定這個版本的 python裡面要有pipenv

先在這個 3.7.6的版本中安裝 pipenv

pip install pipenv

我們這時候執行的 pip 是我們 python 3.7.6 的 pip (相當於 python -m pip)

pipenv install --python $(which python) django==3.0.2

這邊我們使用到下面幾個參數

  • –python 設定要使用的 python 版本 (虛擬環境中的python版本)(因為有時候直接pipenv install 可能沒有抓到 pyenv 設定的版本 所以我為了以防萬一,通常會加上這段)
  • $(which python) 的意思就是 把 ‘which python’ 的 output 結果 取代這個 $() ,得到的結果就會是 pyenv 設定的 python 的目錄 (確保用剛剛設定的python版本執行)
  • django==3.0.2 這邊我們就是要裝 django,並且確定使用 3.0.2 的版本

在安裝package的時候我們都會希望比較重要的package 版本能夠標明清楚,避免之後一樣的程式跑到別的電腦或是AWS之類的上面跑不出來(相容性問題)

接下來我們就可以進入虛擬環境囉!

pipenv shell

這時候我們可以看一下裡面django的版本

django-admin --version
3.0.2

我們可以看到剛剛的安裝好的 3.0.2,這樣就安裝成功囉!

創立Django Project

接下來我們要來新增一個 django 專案

django-admin startproject blog .
  • blog 是專案名稱
  • 後面加一個 ‘.’ 是告訴 django 我要在現在這個位子創建專案,而不要把我的東西另外放進新的資料夾

這時候我們可以用 tree 指令 看一下資料夾裡面多了什麼東西

(沒有tree指令可以去google一下)

tree

目前我們的目錄長這樣

.
├── Pipfile
├── Pipfile.lock
├── blog
│ ├── init.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── db.sqlite3
└── manage.py

  • 剛剛的 Pipfile, Pipfile.lock是我們版本管理用的東西,可以不用管他
  • manage.py 這個檔案就是 我們用來控制 django 各種命令的地方
  • blog這個資料夾就是我們的project基本設定資料,和很多router等等存放的地方
  • db.sqlite3 是我們的簡易 sqlite database
  • asgi.py 是 django 3 新的東西,以前會有一個 wsgi.py,是用來以後我們專案放到網路上讓他可以持續運行的東西
  • 當然還是有wsgi.py
  • urls.py 是用來設定我們的網址(例如: /about, /blogs, /contact 等等)
  • settings.py 就是我們的設定檔
  • __init.py__ 是 django 自己會去調配的初始化設定檔,基本上我們不用理他

這時候我們就可以開始試試看跑我們的server

python manage.py runserver
#或是
./manage.py runserver

這時候打開我們的瀏覽器,進到網址 http://127.0.0.1:8000 或是 localhost:8000會看到類似這樣的畫面

這樣就是python server 開始跑囉!

設定

接下來使用你最愛用的文字編輯器 VSCode, Sublime, vim 等等打開你的專案資料夾就可以開始設定基本的內容!

首先我們可能會希望把語言改成中文,並調整時區

找到 blog/settings.py,並更新下面兩個變數

LANGUAGE_CODE = 'zh-hant'
TIME_ZONE = 'Asia/Taipei'

接下來的設定我們不會動太多,因為現在只是一個最基礎的教學,之後會再寫文章講到如何使用別的database還有其他設定

Migration

接下來我們就快要進入部落格的環節了

首先,注意到從剛剛到現在terminal裡面一直都有一個紅紅的東西

這是告訴我們,我們的database 並沒有做好 migration。白話一點就是說,我們還沒把這個django網站相關的資料寫進我們的database。

在新增部落格之前,當我們一創建好專案,django就有很多資料庫的東西要記錄,例如cookies, sessions, timestamp, 使用者帳號等等,所以他要對我們的database先做一些基本操作 (他會使用ORM做類似 INSERT INTO sessions … 之類的SQL指令,我們不用擔心這塊)

所以這邊我們要先做migration

先Ctrl+C 關閉server

./manage.py migrate

趁這個時候我們也來創建一下 super user 帳號

./manage.py createsuperuser

這邊電子信箱不用打,而我密碼為了方便,設得很簡單,他會跳出提醒但是可以直接按y跳過

接下來回去runserver

./manage.py runserver

這時候就不會再跳出剛才的提醒了

此時我們可以去看一下django的後台:

進到 localhost:8000/admin,會看到這樣的畫面

這時我們就可以用剛剛的帳密登入,登入後看到這個主畫面,就是一個很好用的後台了。

那接下來我們就要回去新增部落格功能,讓後台可以管理新增部落格,之後再呈現到前端上面。

終於要來開始新增部落格的功能了

一樣CTRL+C 關閉 server,之後建立一個叫做 post 的 App

./manage.py startapp post

此時django會幫我們創一個post資料夾,看一下裡面有什麼東西

tree post

post
├── init.py
├── admin.py
├── apps.py
├── migrations
│ └── init.py
├── models.py
├── tests.py
└── views.py

  • 外面的init.py 一樣是一些設定檔(基本上是空的) 先暫時不用理他
  • admin.py 是用來設定部落格跟我們後台之間的 內容呈現 (表單樣式、哪些格子可以編輯等等)
  • apps.py 就是註冊我們這個 post app在用的 class (之後用來在別的地方import)
  • models.py 是用來設定我們的模型 (MVC架構中的M)(這可以決定我們的一篇部落格要有什麼東西,例如:標題、內容、圖片…等)
  • migrations/ 目錄裡面放的是 makemigration 之後產生的ORM指令
  • tests.py 是用來做 unit-testing 的 目前我們先不用理她
  • views.py 是來做 MVC架構中的 View,可能也包含一些 controller的功能(之後會再解釋)

接下來我們要去 blog/settings 註冊我們這個app,讓他可以讀取到

到settings.py 找到 INSTALLED_APPS 這個 list, 加入 ‘post’

INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# 加入 post
'post'
]

這樣我們的app就註冊完成囉(後台的部分還沒)

那我們接著就可以開始來創建我們部落格的模型了

用text editor 進到 models.py

這邊我們建立一個簡單的範本,裡面只有標題跟內容

post/models.py

from django.db import models
class Post(models.Model):
title = models.CharField('標題', max_length=20)
content = models.CharField('內容', max_length=200)
  • 這邊是繼承 models.Model這個模板
  • 然後我們設定title是一個Character Field,用來存放文字,後面 ‘標題’ 是這個title的別稱(會顯示在後台)
  • max_length 是用來設定最多幾個字
  • content的話一樣的道理

接下來,我們要把這樣的資料結構創建schema到資料庫中

回到我們的console

./manage.py makemigrations post

./manage.py makemigrations post
./manage.py migrate
  • 這邊 makemigrations 就會創建一些ORM 的 SQL 指令讓等一下的 migrate執行(用來把資料結構input到資料庫中)
  • migrate就會執行這個動作
  • 如此一來我們的資料庫的結構就和我們的model同步了

admin註冊

接著我們要做的就是去 admins.py 這邊 把我們的 app 表單註冊到後台

post/admin.py

from django.contrib import admin
from .models import Post
class PostAdmin(admin.ModelAdmin):
list_display = ('id', 'title', 'content')
search_fields = ('title', 'content')
admin.site.register(Post, PostAdmin)
  • 這邊第一行,是他預設的admin,目的是把django admin這個 module import 近來
  • 第二行是從當前目錄下的 models.py 引入 剛剛做好的 Post 模型
  • 接下來我們創建一個 PostAdmin class 繼承 ModelAdmin這個模板
  • list_display是在清單目錄下要呈現的內容
  • 包含預設的id(SQL會自己創建)、標題、內容
  • search_fields 是為了要可以使用搜尋功能,可以直接加入這行,讓在admin中以後如果很多篇文章,可以根據標題或內容直接搜尋

接下來回到我們的瀏覽器,到localhost:8000/admin

可以看到已經有Post了,但是這邊我們很想把他用成中文,所以我們要再回到models.py裡面做一些設定

from django.db import models
class Post(models.Model):
class Meta:
verbose_name = '文章'
verbose_name_plural = '文章'
    title = models.CharField('標題', max_length=20, )
content = models.CharField('內容', max_length=200)

def __str__(self):
return self.title

這邊我們用這個 Meta class 用來對 Post模組做一些設定

verbose_name跟verbose_name_plural 決定這個 post 要被以什麼名稱口頭上稱呼他(admin用的)我們中文中都統一設定成 ‘文章’

而 __str__ 則是用來設定到時候每篇文章要用誰來稱呼他(預設會用 id,我們想要每篇文章用標題稱呼他)

這時回到我們的admin,可以看到 Posts被叫成 ‘文章了’,而上面的 POST 我們需要動到他模板的template,就先暫時不管他。

我們現在可以去新增幾篇文章試試看

現在可以看到我們現在列表中有我們剛剛新增的內容了,而這些內容都會被儲存在我們的 db.sqlite3這個檔案中(其實有安全性的問題,所以大型專案不建議使用 sqlite)

把內容推播出去

接下來我們要把內容推播出去,利用views.py決定我們要呈現的內容(可能有點含糊,跟著做就對了)

這裡我們要用最簡單,寫起來最快的 Class-Based Views (CBV),有別於一些網路上其他的文章,都是使用functional views 這邊我們會用比較新的功能來快速產生views

from django.views.generic import ListView, DetailView
from .models import Post
class PostListView(ListView):
model = Post
template_name = 'post_list.html'
class PostDetailView(DetailView):
model = Post
template_name = 'post_detail.html'
  • 這邊先 import 我們要繼承的 快速view產生模板 ListView和 DetailView
  • 接著和 admin.py 的時候一樣 加入我們的model當 reference
  • 接下來我要有一個列表View(用來呈現文章列表)
  • 再一個 Detail View (用來呈現單一內容)
  • 再來裡面的 model就是 用我們的模型做reference
  • template_name 則是告訴 django我們等一下要用到的 html 模板檔案

在前端呈現這些文章

接下來就是我們的呈現環節了,要產生真正的網頁,並且把這些內容呈現出來

這時候我們就需要設定所謂的 template(模板)

並且設定 routes(路徑)在 urls.py 中

先創立模板

回到根目錄(django-blog-demo) 創立一個 templates 資料夾

在裡面建立一個base.html

接下來我們要用到 Bootstrap 這個CSS framework 來快速做出我們的模板

點擊上面的連結進到Bootstrap,之後點get started 找到這個 starter template 然後把他複製到我們的 base.html

接下來我們先把剛剛可愛的django已經安裝成功頁面換掉

接著再創建兩個檔案分別為 post_detail.html和post_list.html,

裡面目前都放這樣的內容

{% extends 'base.html' %}

意思是從 base.html這個檔案去延伸

目前我們的目錄結構長這樣:

.
├── Pipfile
├── Pipfile.lock
├── blog
│ ├── init.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── db.sqlite3
├── manage.py
├── post
│ ├── init.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ └── init.py
│ ├── models.py
│ ├── tests.py
│ └── views.py
└── templates
├── base.html
├── post_detail.html
└── post_list.html

更新: 這時我們要去 blog/settings.py 把templats/ 這個資料夾加到template engine的讀取清單中

TEMPLATES = [
 {
 ‘BACKEND’: ‘django.template.backends.django.DjangoTemplates’,
 # 加入templates目錄
 ‘DIRS’: [os.path.join(BASE_DIR, ‘templates’)],
#…

接著我們要去 blog/urls.py 讓這兩個頁面會呈現出來

blog/urls.py

from django.contrib import admin
from django.urls import path
from post.views import PostListView, PostDetailView
urlpatterns = [
path('admin/', admin.site.urls),
path('', PostListView.as_view()),
path('<pk>', PostDetailView.as_view())
]

這邊就是要讓 localhost:8000 呈現 PostListView,而 localhost:8000/1 呈現第一篇文章,依此類推

這個 <pk> 代表 primary key 就是用來表示文章編號的代名詞而已

接下來我們重新啟動server就可以看到不一樣囉

現在打開 localhost:8000, localhost:8000/1, localhost:8000/2 都會看到這樣的頁面,這就表示模板讀取成功囉!

調整模板

接著就要開始在模板裡面呈現東西了!

django 使用的是 jinja template 裡面有很多不同的邏輯功能,可以讓我們呈現網站非常快速,詳情可以上django 官網

先回到 base.html

<!DOCTYPE html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<!-- Bootstrap CSS -->
<link
rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"
integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z"
crossorigin="anonymous"
/>
<title>Django | 簡易部落格</title>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container">
<a class="navbar-brand" href="/">簡易Django部落格</a>
</div>
</nav>
{% block body %}{% endblock %}
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script
src="https://code.jquery.com/jquery-3.5.1.slim.min.js"
integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrXaRkfj"
crossorigin="anonymous"
></script>
<script
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js"
integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN"
crossorigin="anonymous"
></script>
<script
src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"
integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV"
crossorigin="anonymous"
></script>
</body>
</html>

調整title,並把 body 中的 hello world 換成 {% block body %}{% endblock %}

並且在上面加一個簡單的bootstrap 導覽列 (navbar)

這就是我們等一下要去 post_detail跟post_list中取代的部分,而外面的部分等一下在裡面就不用重打。

這時再回去看我們的網站就有基本的樣式囉

部落格列表

回到post_list.html

{% extends 'base.html' %} 
{% block body %}
<div class="container mt-5">
<h1>文章列表</h1>
<div class="row">
{% for post in object_list %}
<div class="col-md-6">
<div class="card my-3">
<div class="card-body">
<h5 class="card-title">{{ post.title }}</h5>
<p class="card-text text-muted">
{{ post.content }}
</p>
<a href="/{{ post.id }}" class="card-link">閱讀更多</a>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock %}
  • extends 代表繼承 base.html (剛剛的導覽列那些)
  • 接下來這些div就是bootstrap的一些classes,因為本篇不是bootstrap的文章,就不多做介紹了,之後以會再做bootstrap的簡易教學
  • {%block body%} 的意思就是編輯剛剛我們 base.html裡面那個區塊(在裡面加東西)。後面記得要加 {%endblock%}
  • {%for%}{%endfor%} 就是回圈 在裡面可以去從一個陣列單獨的一個一個東西抓出來。
  • 像這邊我們的例子就是要從 object_list 這裡抓東西 (這就是我們的文章列表)
  • 注意這邊 object_list 是固定的字眼,如果想要把它換成 posts之類的,要到views裡面設定 (可以查一下)
  • 這有點像 javascript 的 map 或是 forEach 那種概念
  • 之後我們就是呈現一張卡片,在標題的地方呈現 {{ post.title }} 他就會換成文章標題
  • content一樣的道理
  • 而在 <a href=”/{{ post.id}}”> 這邊我們是要讓網址變成 href=”/1″ href=”/2″ 這樣子,讓我們連到 等一下的detail頁面

如果以上不懂沒有關係,這是跟html和bootstrap比較有關,可以先複製貼上看一下效果

接下來點進去 閱讀更多就會到我們的detail頁面,我們可以開始做我們的detail囉!

文章detail頁面

這邊我們用得很簡單,以下看 post_detail.html

{% extends 'base.html' %}
{% block body %}
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/">文章列表</a></li>
<li class="breadcrumb-item active" aria-current="page">
{{ post.title }}
</li>
</ol>
</nav>
<div class="container mt-2">
<h1>{{ post.title }}</h1>
<hr />
<p>{{ post.content }}</p>
</div>
{% endblock %}

就只是簡單呈現 title跟 content,並且在上面用一個breadcrumb目錄來方便導覽。

基本上這樣就大功告成囉!

ps. terminal 裡面有一個 這樣的 error 會產生的原因是因為我們偷懶,現在post urls.py目錄是直接設在根目錄下面 localhost:8000/ ,所以當他請求 favicon.ico 的時候原本是要去抓網站logo 變成跑去抓一篇 id 為 favicon.ico 的文章。

不過這在我們現在的project中並不構成影響,可以不用理會!

最後,如果有不懂的東西歡迎Facebook, medium 留言提問

完整github repo: https://github.com/knhn1004/django-blog-demo

之後會再出如何部署成一個真正的網站等等教學,也會有如何用django寫 restful API 搭配 jquery 或 react 等框架作為前端 的教學

這只是一篇最基礎的教學,用文章可能不太清楚,之後打算會有影片series放在youtube上,年底可能也會有一些線上課程上線,盡請期待!

謝謝大家!

作者: 周家鋐 — — 浚鋐網路科技有限公司 創辦人

By Chiahong Chou on .

Canonical link

Exported from Medium on August 19, 2021.

Write a comment