Django 是 Python 社區的兩大最受歡迎的 Web 框架之一(另外一個是 Flask)。憑藉功能強大的腳手架和諸多開箱即用的組件,用 Django 搭建 Web 應用快速而又省力。然而,也正是由於過於強大,想要駕馭它須要花費很多的力氣。本文將經過實現一個新聞發佈網站帶你快速熟悉 Django 框架,讓你可以騎上這匹快馬,在 Web 開發的戰場上盡情馳騁。html
提示前端
這篇文章寫做時用的是 Django 2.x 版本,發表時已經推出了 3.x 版本。不過通過筆者測試,這篇文章中的代碼對於 2.x 和 3.x 都適用哦!python
Django 由 Adrian Holovaty 和 Simon Willison 在 2003 年的秋天寫成,並在 2005 年正式發佈。他們倆當時爲一個新聞報社製做網站,對快速開發有着比較高的需求,而且但願可以在開發的同時也可以讓非技術人員爲網站添加內容。因而這也使得 Django 具有了兩項鮮明的特色:數據庫
Django 的名字取自吉他手 Django Reinhardt,發音爲 JANG-goh(諧音「尖狗」),但實際上 Django 的吉祥物是一隻長着翅膀的小馬。django
在這篇教程中,咱們也將向 Django 的起源致敬——手把手帶你開發一個新聞發佈網站,而且能夠從後臺管理系統中添加新聞,展現到網站首頁上。瀏覽器
本教程假定你已經知道了:bash
讀完這篇教程後,你將掌握 Django MTV 框架的精髓:服務器
雖然 Django 還有不少知識點,可是理解了 MTV,後面的知識點學習起來也就輕鬆多啦。session
本文假定你已經安裝好了 Python 3 和 pip,那麼能夠直接用 pip 安裝 Django:app
pip install django
複製代碼
直接用 pip 在全局安裝 Django 的確不是一個很好的作法,用虛擬環境更符合最佳實踐。爲了減小初學者們的認知負擔,在這裏就簡化了安裝過程。熟悉 pipenv 等虛擬環境工具的老司機固然能夠自行使用哈。
安裝好 Django 後,咱們用 Django 自帶的腳手架工具 django-admin 建立項目:
django-admin startproject django_news
cd django_news
複製代碼
生成的項目骨架及每一個文件的做用以下所示:
django_news
├── django_news // 項目全局文件目錄
│ ├── __init__.py
│ ├── settings.py // 全局配置
│ ├── urls.py // 全局路由
│ └── wsgi.py // WSGI服務接口(暫時不用糾結這個是神馬)
└── manage.py // 項目管理腳本
複製代碼
咱們使用 manage.py 來運行開發服務器(Development Server):
python manage.py runserver
複製代碼
提示
細心的你會發現出現了一行鮮紅色的提示:You have 17 unapplied migration(s)...(省略 n 個字符)。不用擔憂,咱們會在接下來的步驟中詳細講解前因後果。
按照提示,咱們經過瀏覽器訪問 localhost:8000,能夠看到歡迎界面:
提示
Django 開發服務器能夠保持開啓,而且後面修改代碼會自動從新加載,很是方便。後面運行其餘命令時,再打開一個終端(命令行)便可。
一切準備就緒,繮繩已在你手中!
在上一節中咱們講到,Django 是一個高度模塊化的框架。具體而言,一個 Django 應用由多個子應用組成,咱們通常稱之爲 App(注意不是咱們常說的移動應用 APP,而是 Application 的簡寫),以下圖所示。
Django App 通常分爲三大類(根據來源):
全部的 Django 應用都在 django_news/settings.py 的 INSTALLED_APPS
列表中定義:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
複製代碼
話很少說,讓咱們來建立第一個自定義 App,名稱爲 news:
python manage.py startapp news
複製代碼
生成的 news 應用文件夾結構以下所示:
news // news 應用目錄
├── __init__.py // 初始化模塊
├── admin.py // 後臺管理配置
├── apps.py // 應用配置
├── migrations // 數據庫遷移文件目錄
│ └── __init__.py // 數據庫遷移初始化模塊
├── models.py // 數據模型
├── tests.py // 單元測試
└── views.py // 視圖
複製代碼
這個子目錄裏面乍一看好多文件啊!這是由於 Django 始終堅持解耦的原則——儘可能減小代碼之間的耦合,把不相關的代碼拆成多個模塊,讓同一個模塊具備內聚性。相信我,等到後面慢慢熟悉以後,你會對每個模塊都瞭如指掌的。
實際上,每一個 Django App 的組織結構符合 Django 的 MTV 法則——Model(模型)+ Template(模板)+ View(視圖)。MTV 與你們比較熟悉的 MVC 在思想上很是類似,可是命名有比較大的出入,以下表所示:
你們熟知的 View,在 Django 裏面表明的是業務邏輯,也就是 MVC 中的控制器哦!
最後,咱們在 settings.py 中將 news 應用加入 INSTALLED_APPS
中:
# ...
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'news',
]
複製代碼
至此,咱們已經建立了第一個 Django 應用!可是如今這個應用尚未任何內容,咱們接下來將逐步完善這個應用。
也許你已經注意到,經過訪問 localhost:8000/admin 已經能夠訪問後臺管理系統了(雖然會跳轉到登陸界面)。接下來,咱們也但願可以訪問到剛纔建立的 news 應用。所以,這一步中咱們將:
Django 的路由系統是由全局路由和子應用路由組成。簡單來講,根據用戶輸入的 URL,全局路由表進行匹配並選擇正確的子應用路由,再由所選擇的子應用路由匹配並選擇正確的視圖(View)。整個流程以下圖所示:
例如,用戶訪問 example.com/apple/buy,而後全局路由根據 /apple/buy 先選擇 apple 的路由表,再從 apple 路由表中根據 /buy 選擇 /buy 路由,而後執行 /buy 對應的 BuyView 視圖,返回給用戶結果。
對視圖訪問的流程大體瞭解以後,咱們就能夠開始動手了。首先打開 news/views.py,寫一個簡單的視圖函數,返回一串 Hello World!:
from django.http import HttpResponse
def index(request):
return HttpResponse('Hello World!')
複製代碼
上面這個 index
函數能夠說是一個最簡單的視圖函數了,實際大部分應用的視圖要比這複雜得多。Django 同時支持基於函數的視圖(FBV,Function-based View)和基於類的視圖(CBV,Class-based View),這裏顯然是 FBV,接收一個 request
請求對象做爲參數,返回了一個 HttpResponse
對象。
接着,咱們要讓路由系統可以訪問到剛纔寫好的視圖函數。所以先實現子應用 news 的路由表,建立 news/urls.py 文件以下:
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
]
複製代碼
每個 Django 路由表模塊(urls.py)中都約定必須包含一個 urlpatterns
列表用來存放路由映射表。列表中每一個元素是一個用 django.urls.path
函數封裝好的路由映射,一般接收如下三個參數:
route
:必須,即實際的訪問路由,空字符串等於 /
,即空路由view
:必須,該路由將要訪問的視圖name
:可選,該路由的名稱,方便後續在模板中使用咱們將剛剛寫好的 news 路由表接入全局路由表。因爲咱們但願新聞可以展現在首頁(即經過 /
就能訪問,無需 /news
),所以 news 應用路由在全局路由中的 URL 是一個空字符串。在 django_news/urls.py 中修改以下:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('news.urls')),
]
複製代碼
這裏使用 django.urls.include
函數將 news 應用的路由表接入進來,而且 include
函數的參數是路由模塊路徑的字符串 news.urls
,省去了手動 import
的麻煩。
注意
添加路由規則時順序是很重要的,由於在嘗試匹配時會按照從上到下的順序進行,所以應該把最模糊的路由(即空路由)放在最下面。
若是你開發服務器還在運行(若是沒有能夠再打開),訪問 localhost:8000,就能夠看到那一串熟悉的字符了:
上一步中,咱們學會了如何實現視圖,並將其接入路由配置中,使其可以被用戶訪問。接下來,咱們將實現一個 Django 模板做爲網頁前端,從而給用戶呈現更豐富的內容。
提示
若是你有過其餘模板(或者相似技術)的開發經驗,例如 Jinja、EJS 或是 JSP 等,對 Django 模板會有一種似曾相識的感受。若是你不瞭解什麼是模板引擎,也不用擔憂,簡單的理解就是一個能夠填充內容、甚至可以加入代碼邏輯的相似 HTML 文檔,最終會被轉換成瀏覽器可以識別的 HTML 文檔。
Django 模板本質上是一個 HTML 文檔,只不過經過一些特殊的語法實現數據的填充。這裏咱們講解一下最經常使用的三個語法:
表達式插值
最經常使用的語法,沒有之一。經過在一對花括號 {{}}
放入一個表達式,就可以在視圖中傳入表達式中變量的內容,並最終渲染成包含變量具體內容的 HTML 代碼。須要注意的是,所支持的表達式僅支持如下形式(能夠自由組合):
<!-- 單個變量 -->
{{ variable }}
<!-- 獲取字典的鍵或對象的屬性 -->
{{ dict.key }}
{{ object.attribute }}
<!-- 獲取列表中的某個元素 -->
{{ list.0 }}
複製代碼
例如,模板這樣寫:
<h1>{{ name }}</h1>
<p>{{ news.title }}</p>
<p>{{ news.visitors.0 }}</p>
複製代碼
若是咱們在視圖中傳入如下上下文字典(Context Dictionary):
{
'name': 'Tuture',
'news': {
'title': 'Hello World',
'visitors': ['Tom', 'Marc'],
}
}
複製代碼
那麼最終渲染成的 HTML 代碼就是:
<h1>Tuture</h1>
<p>Hello World</p>
<p>Tom</p>
複製代碼
條件語句
條件語句的定義以下:
{% if is_true %}
<h1>It is true!</h1>
{% else %}
<h1>It is false!</h1>
{% endif %}
複製代碼
若是變量 is_true
爲真,那麼最終渲染出來的就是 <h1>It is true!</h1>
,不然就是 <h1>It is false!</h1>
。注意:整個條件語句必須以 {% endif %}
結束,而且 {% else %}
是可選的。
循環語句
循環語句用來在模板上展現任意長的列表內容。其語法以下:
{% for elem in some_list %}
<p>{{ elem }}</p>
{% endfor %}
複製代碼
若是傳入的 some_list
爲 ['Apple', 'Banana', 'Orange']
,那麼渲染出的 HTML 代碼就是:
<p>Apple</p>
<p>Banana</p>
<p>Orange</p>
複製代碼
到了動手時間了,咱們先實現第一個 Django 模板。在 news 目錄中建立一個 templates 目錄,再在 templates 目錄中建立一個 news 目錄,並在內層的 news 目錄中建立 index.html 文件:
mkdir -p news/templates/news
touch news/templates/news/index.html
複製代碼
思考
聽上去很麻煩,只建立
news/templates
,而後把模板放裏面不就行了,爲何還要再建立一個 news 目錄?這是因爲 Django 的模板查找機制會將全部應用裏面的模板所有收集到一塊兒,若是兩個模板的名字衝突,就會致使其中一個模板不能被正確訪問。若是放在 news 子文件夾裏面,就可以經過news/index.html
訪問,經過命名空間的機制避免了衝突。
模板的代碼以下:
{% if news_list %}
<ul>
{% for elem in news_list %}
<li>
<h3>{{ elem.title }}</h3>
<p>{{ elem.content }}</p>
</li>
{% endfor %}
</ul>
{% else %}
<p>暫無新聞</p>
{% endif %}
複製代碼
這短短几行模板代碼卻很好地覆蓋了咱們剛剛講述的三個模板語法:表達式插值、條件語句和循環語句。若是忘記其中某個地方是什麼意思的話,翻上去看看吧!
完成模板的編寫後,咱們要在視圖中對其進行渲染。打開 news/views.py 文件,修改代碼以下:
from django.shortcuts import render
def index(request):
context = {
'news_list': [
{
"title": "圖雀寫做工具推出了新的版本",
"content": "隨隨便便就能寫出一篇好教程,真的很神奇",
},
{
"title": "圖雀社區正式推出快速入門系列教程",
"content": "一杯茶的功夫,讓你快速上手,絕無擔心",
},
]
}
return render(request, 'news/index.html', context=context)
複製代碼
這裏咱們調用 django.shortcuts.render
函數來渲染模板,這個函數一般接受三個參數(有其餘參數,可是這裏咱們不關心):
request
:請求對象,直接把視圖的參數 request
傳進來就能夠template_name
:模板名稱,這裏就是咱們剛剛建立的 news/index.html
context
:傳入模板的上下文對象,必須是一個字典,字典中的每一個鍵對應模板中的變量。這裏咱們弄了些假數據,僞裝是從數據庫裏面取來的。再訪問 localhost:8000,看一下咱們的首頁是否是有內容了:
完美!
Django 的 MTV,咱們已經講了 T(Template)和 V(View),如今來到了最後一關: M(Model)了。數據模型是 Django 入門最大的難點,消化這一步的內容須要花點力氣,可是相信我,當你邁過 M 這最後一關,你便能真正上手 Django 開發了!下面咱們先介紹一下 Django 的數據模型設計。
Django 在數據模型方面的設計堪稱典範,列舉一些閃光點:
對於初學者而言,咱們暫且選擇默認的 SQLite 數據庫,省去了配置數據庫的煩惱。在後面的進階教程中,咱們會切換到其餘適合生產環境的數據庫。
簡單來講,ORM 可以將面向對象的代碼轉換成相應的 SQL 語句,從而對數據庫進行操做。SQL 是用於訪問和處理數據庫的標準的計算機語言,可是直接寫在代碼裏面顯然難以維護,並且對使用者的要求也很是高,寫的糟糕的 SQL 代碼查詢效率很是低下。所以,使用設計良好的 ORM 不只讓代碼可讀性更好,也能幫助開發者進行查詢優化,節省很多力氣。
咱們來看一些簡單的 Django ORM 例子:
# 查詢全部模型
# 等價於 SELECT * FROM Blog
Blog.objects.all()
# 查詢單個模型
# 等價於 SELECT * FROM Blog WHERE ID=1
Blog.objects.get(id=1)
# 添加單個模型
# 等價於 INSERT INTO Blog (title, content) VALUES ('hello', 'world')
blog = Blog(title='hello', content='world')
blog.save()
複製代碼
有木有感受操做起來比 SQL 方便不少呢?
數據庫遷移是指將用 Django 定義的模型轉換成 SQL 代碼(即遷移文件),並在數據庫中進行建表操做(或更新表)。看下面這張圖就知道了:
通常的開發流程就是這樣:
makemigrations
命令建立遷移文件(存儲在子應用的 migrations 目錄裏面)migrate
命令執行遷移終於到了動手的環節。咱們首先定義數據模型 Post
,包括標題 title
字段和 content
字段,代碼以下:
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
def __str__(self):
return self.title
複製代碼
定義好以後,運行如下命令建立遷移文件:
python manage.py makemigrations
複製代碼
能夠看到輸出以下:
Migrations for 'news':
news/migrations/0001_initial.py
- Create model Post
複製代碼
而且成功地自動建立了 news/migrations/0001_initial.py 遷移腳本。接着咱們進行數據庫遷移:
python manage.py migrate
複製代碼
輸出以下圖所示:
數據庫遷移完成後,咱們就能夠建立用於登陸後臺管理的超級用戶:
python manage.py createsuperuser
複製代碼
按照提示填寫用戶名和密碼便可。而後訪問 localhost:8000/admin,進入後臺系統的登陸頁面:
填入剛纔設置的用戶名和密碼,進入後臺管理頁面:
咦,咱們剛纔建立的 news 應用還有 Post 模型去哪了?
那是由於咱們沒有實現 news 應用的後臺管理接口。在 news/admin.py 中填入代碼以下:
from django.contrib import admin
from .models import Post
admin.site.register(Post)
複製代碼
再次進入後臺管理系統,能夠看到咱們的 news 應用和 Post 模型了:
點擊 Posts 一欄的 +Add 按鈕,開始添加新聞(內容隨意):
大概添加個兩三條新聞就差很少啦。你也能夠進一步探索後臺管理系統,包括修改新聞、添加用戶等等,均可以。
最後,咱們在視圖中加入從數據庫中查詢的代碼:
from django.shortcuts import render
from .models import Post
def index(request):
context = { 'news_list': Post.objects.all() }
return render(request, 'news/index.html', context=context)
複製代碼
訪問網站首頁,能夠看到剛纔在後臺管理系統添加的新聞了:
大功告成!在這篇教程中,咱們完成了一個新聞發佈網站,而且能夠從後臺管理系統中添加新聞,最終展現到咱們的網站首頁上。
但願這篇教程可以讓你對 Django 最重要的一些概念和操做有了基本的瞭解。Django 還有不少不少的高級玩法,例如數據模型中的高級查詢、字段索引、更換數據庫等等,模板中的繼承機制、內部標籤等等,還有視圖中如何處理各種請求(POST、PUT等),咱們會在後續更多教程中逐一爲你們講解,不見不散!
想要學習更多精彩的實戰技術教程?來圖雀社區逛逛吧。