作者:Hubery 時間:2018.7.24
0 前言 = 瞎說
能寫web的語言有好多。python算是難度較低,入門較快的指令碼語言。Django是python的web框架,詳情不多敘,見:
# Django歷史---------->>>>>這段可以不看。
在 Web 早期階段,開發者手動編寫每個頁面。更新網站要編輯 HTML;
重新設計要重新制作每一個網頁,而 且一次只能改一個網頁。
隨著網站體量的增大,這種方式立馬變得繁瑣、浪費時間,最終變得不切實際。
NCSA(National Center for Supercomputing Applications,國家超級計算應用中心,
第一款圖形 Web 瀏覽器 Mosaic 就是在這裡開發出來的)一群富於創新的黑客
解決了這個問題,他們讓 Web 伺服器派生外部程式, 動態生成 HTML。
他們把這一協議稱為通用閘道器介面(Common Gateway Interface,CGI),
自此,Web 完全 變了樣。如今,很難想象 CGI 帶來的變革:
CGI 不再把 HTML 頁面視作硬碟中儲存的檔案,而是把頁面看 做資源,
可以按需動態生成。
CGI 的開發促使了第一代動態網站的出現。然而,CGI 自身也有問題:
CGI 指令碼包含大量重複的樣板程式碼, 導致程式碼難以複用,而且新手難以編寫和理解。
PHP 解決了這些問題中的多數,在 Web 開發界引起了一陣風暴。
PHP 現在是建立動態網站最流行的工具,
多門類似的語言(ASP、JSP,等等)都參照了 PHP 的設計原則。
PHP 的主要創新是易於使用:PHP 程式碼直 接嵌入普通的 HTML 中;
對學過 HTML 的人來說,學習曲線極為平緩。
但是,PHP 也有自身的問題:就是因為易於使用,寫出的程式碼凌亂、重複,設計不周。
更糟的是,PHP 沒有 為程式設計師提供多少防止安全漏洞的保護機制,
很多 PHP 開發者意識到這一點再去學習相關的知識就晚了。
上述問題以及類似的缺陷直接促使了“第三代”Web 開發框架的湧現。
Web 開發的新方式也提升了人們的雄 心,現在 Web 開發者每天所做的工作越來越多。
Django 就是為了迎接這些雄心而誕生的。
Django 是從真實的應用中成長起來的,由美國堪薩斯州勞倫斯的一個 Web 開發團隊編寫。
它誕生於 2003 年秋天,那時 Lawrence Journal-World 報社的 Web 開發者
Adrian Holovaty 和 Simon Willison 在嘗試使用 Python 構建應用。
World Online 團隊負責製作和維護本地的幾個新聞網站,在新聞界特有的快節奏開發
環境中逐漸發展壯大。
那些網站(包括 LJWorld.com、Lawrence.com 和 KUsports.com)的
記者(和管理層)不斷要求增加功能,
而且整個應用要在緊張的週期內快速開發出來,通常只有幾天或幾小時。
因此,Simon 和 Adrian 別無他法,只 能開發一個節省時間的 Web 開發框架,
這樣他們才能在極短的截止日期之前構建出易於維護的應用。
經過一段時間的開發後,那個框架已經足夠驅動世界上最大的線上網站了。
2005 年夏天,團隊(彼時 Jacob Kaplan-Moss 已經加入)決定把框架作為開源軟體釋出出來。
他們在 2005 年 7 月釋出了那個框架,將其命名 為 Django——取自爵士吉他手 Django Reinhardt。
這段歷史相當重要,因為說清了兩件要事。首先是 Django 的“發力點”。
Django 誕生於新聞界,因此它提供了幾個特別適合“內容型”網站使用的功能(如管理後臺)。
這些功能適合 Amazon.com、craigslist.org 和 washingtonpost.com 這樣動態的資料庫驅動型網站使用。
不過,不要因此而灰心。雖然 Django 特別適合開發這種網站,
但是這並沒有阻礙它成為開發任何動態網站的有效工具。(某些方面“特別”高效與某些方面不高效是由區別的。)
第二點是,Django 最初的理念塑造了開源社群的文化。Django是從真實程式碼中提取出來的,
而不是科研專案或商業產品,它專注於解決Django的開發者自身所面對的問題。因此,
Django一直在積極改進,幾乎每一天都有變化。
Django框架的維護者一心確保它能節省開發者的時間,確保開發出的應用易於維護,
而且在高負載下的效能良好。
使用 Django 能在極短的時間內構建全面動態的網站。Django 的主旨是讓你集中精力在有趣
的工作上,減輕 重複勞作的痛苦。為此,它為常用的 Web 開發模式提供了高層抽象,為常
見的程式設計任務提供了捷徑,還為解 決問題提供了清晰的約定。與此同時,Django 儘量做到
不擋路,允許你在必要時脫離框架。
複製程式碼
寫專案就好比騎自行車,看完上面的背景,接下來就上程式碼體驗了,直接開擼。
按照草擬的專案結構,大致上有5塊內容:
- 啟動專案
- 新增使用者
- 檔案安全
- 快取列表
- 專案部署
專案初步規劃要完成一個電影列表/詳情的檢視,評論,投票,得分,支援檔案上傳,內容安全, 註冊/登陸/登出,內容快取等。
大致構思了下,要寫好還是需要處理不少細節問題的。比如瞭解下Django的編碼套路,處理關聯關係,資料庫的CRUD等,一步步的來吧。
本文主要實現專案的第一部分,啟動專案
部分。
1 環境準備
- 安裝python 預設的就是最新的3.6.*
不同平臺安裝方式可能不同,自行百度一個。 www.python.org/downloads/
- 安裝django 預設的是最新的2.*
pip:pip install packages縮寫,是python的包管理工具,用於安裝python包。
複製程式碼
pip install django // 安裝最新版
pip install django==2.*.* // 安裝具體版本
複製程式碼
- 安裝pycharm IDE
從網上下載一個就行了,然後搜一個啟用碼完事兒。
- 安裝mysql
什麼平臺都一樣,mac/linux/win,都記得配置環境變數
;我用的Mac環境。
這個軟體安裝部分,還是得多百度,可能存在平臺差異。
順便提一句,如果有任何語言的開發基礎,其實回頭看python都會覺得簡單,老說沒有python基礎,看書啊! 突然宋小寶的畫面出現了,哈哈哈。有時候一門語言可能知道40%左右就可以開幹了,邊擼邊回頭翻書就好。
2 建立專案 MyMovie
命令列建立
django-admin
cd workspace
django-admin startproject MyMovie
複製程式碼
可以用pycharm建立 預設專案結構
MyMovie\
MyMovie\
__init__.py\
settings.py\
urls.py\
wsgi.py\
manage.py\
複製程式碼
專案結構解析:
-
外層MyMovie根目錄是專案容器。這個外層名稱對Django來說沒什麼用,可以根據喜好改名。
-
根目錄manage.py 是一個命令列實用指令碼,可以根據不同方式與Django專案互動。如資料庫遷移,跑測試,啟動開發server等,會經常用到manage.py。
-
內層MyMovie目錄,是專案的Python包。匯入這裡面的內容時要用該目錄名稱。如MyMovie.urls。
-
內層MyMovie/init.py是個空檔案,目的是讓Python知道該目錄是Python包。
-
內層MyMovie/settings.py是Django專案的配置。
-
內層MyMovie/urls.py是整個專案的URL配置,即Django驅動的網站的目錄。每個Web app的請求都會被指向urls檔案中已配置過的匹配的第一個view。
-
內層MyMovie/wsgi.py是相容WSGI的web伺服器的介面,用於服務專案。SWGI,Web Server Gateway Interface,讓Django專案與web伺服器互相互動的介面。如將Django專案部署到Docker上。
2.1 解析settings.py檔案
TIME_ZONE = 'UTC'. # 時區
# Django中自帶的啟用的全部Django應用,自建的app也要配置到這裡面
INSTALLED_APPS = [
'django.contrib.admin', # 管理後臺
'django.contrib.auth', # 身份驗證系統
'django.contrib.contenttypes', # 內容型別框架
'django.contrib.sessions', # 會話框架
'django.contrib.messages', # 訊息框架
'django.contrib.staticfiles', # 管理靜態檔案的框架
]
複製程式碼
Django專案中預設包含這些app,為常見場景做的約定。 如果用到資料庫表,使用之前要在資料庫中先建表,為此,執行以下命令:
python manage.py migrate
複製程式碼
資料庫配置 用mysql示例,前提是系統中有資料庫,且user/password等資訊都OK; 終端建立mymovie資料庫
# 命令列進入mysql 並輸入密碼
mysql -uroot -p
# 檢視當前存在的資料庫
mysql> show databases;
# 建立資料庫
mysql> create database mymovie;
Query OK, 1 row affected (0.10 sec)
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| blog_project_db |
| information_schema |
| mymdb |
| mymovie |
| mysql |
| performance_schema |
| sys |
+--------------------+
7 rows in set (0.00 sec)
mysql>
複製程式碼
在MyMovie/init.py檔案中配置mysql資料庫:
import pymysql
pymysql.install_as_MySQLdb()
複製程式碼
settings.py檔案中配置mysql
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # 資料庫引擎
'NAME': 'mymovie',
'USER': 'root',
'PASSWORD': 'root@123456',
'HOST': '127.0.0.1',
'PORT': '3306',
}
}
複製程式碼
2.2 建立core應用
Django應用遵循MVT模式,與傳統的MVC模式無異,只是叫法不同:
MVT模型 | 職責說明 |
---|---|
Models | 用來處理資料庫讀寫操作 |
Views | 用來處理HTTP請求,啟動模型的操作然後返回HTTP響應資料 |
Templates | 用來展示響應內容 |
2.2.1 命令列 建立app
cd MyMovie
python manage.py startapp core
複製程式碼
2.2.2 註冊新建的core app
settings.py檔案中 新增剛建立的core app 每個註冊的app後面必須帶【,】
INSTALLED_APPS = [
'core',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
複製程式碼
2.2.3 建立Model Movie
Django model是從Model衍生繼承過來的,有多個Fields欄位。
資料庫方面,一個Model對應一個資料庫表,一個Model例項對應一行,Field欄位對應一列。
Django model | 對應資料庫 |
---|---|
Model類 | 表 |
Model例項 | 行 |
Field欄位 | 列 |
用Django的ORM,用Python和Django來寫model類來處理資料庫,而不是直接寫SQL語句。
core/models.py
from django.db import models
# 編寫第一個model Movie
class Movie(models.Model):
NOT_RATED = 0
RATED_G = 1
RATED_PG = 2
RATED_R = 3
# 評分 級別
RATINGS = (
(NOT_RATED, 'NR - 沒有評分'),
(RATED_G, 'G - 普通觀眾'),
(RATED_PG, 'PG - 父母的引導和規範'),
(RATED_R, 'R - 限制級'),
)
title = models.CharField(max_length=140)
plot = models.TextField()
year = models.PositiveIntegerField()
rating = models.IntegerField(
choices=RATINGS,
default=NOT_RATED
)
runtime = models.PositiveIntegerField()
website = models.URLField(blank=True)
def __str__(self):
return '{} ({})'.format(self.title, self.year)
複製程式碼
-
Movie繼承自models.Model,
models.Model
是所有Django模型的基類。 -
title
會轉成資料庫表中的一個列欄位,長度為140,型別為varchar; -
plot
會轉成資料庫的text
列, -
year
會轉成資料庫的integer
列,Django儲存之前會驗證資料,確保是0或者更高。 -
rating
多選列。是一個integer列。可選引數choices有一個集合的遊標。
Django會向model種新增一個例項方法:get_rating_display()
,返回儲存在模型中符合第二個引數條件的資料。
-
runtime
與year一樣。 -
website
大多數資料庫列欄位沒有URL型別
,但資料驅動web應用經常需要儲存URL。URLField
預設是長度200的varchar
列,也可以通過max_length引數設定。URLField自帶了驗證邏輯,可以鑑別該URL是否為有效。blank引數由admin應用使用,用來確認該引數是否可以為空。
__str__(self)
方法,將Django模型轉化成視覺化的字串,類似java中的toString()。有助於debug以及輸出物件內容。
Django的ORM會自動增加一個字增長的列:id
。我們無需關心該欄位。
Django的DRY理論:Donnot Repeat Yourself
2.2.4 資料庫遷移
我們有了模型,需要在資料庫中建立匹配該模型的表table。
用Django可以生成該表。
cd MyMovie
python manage.py makemigrations
# 一鍵操作 遷移所有的app的資料庫 根據model建表
python manage.py migrate
複製程式碼
終端執行結果
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying sessions.0001_initial... OK
複製程式碼
執行完之後,資料庫中就會生成一張表: core_movie
mysql> show tables;
+----------------------------+
| Tables_in_mymovie |
+----------------------------+
| auth_group |
| auth_group_permissions |
| auth_permission |
| auth_user |
| auth_user_groups |
| auth_user_user_permissions |
| core_movie |
| django_admin_log |
| django_content_type |
| django_migrations |
| django_session |
+----------------------------+
11 rows in set (0.00 sec)
mysql>
複製程式碼
在Django的應用中才會存在資料庫遷移,而不是在專案目錄。
2.2.5 建立第一個Movie
類似python,Django提供了一個互動式的REPL來嘗試一下。 Django的互動指令碼完全連結資料庫,所以我們可以在shell中進行model的增刪改查。
cd MyMovie
python manage.py shell
# 執行CRUD
複製程式碼
也可以在pycharm的python console視窗中進行操作,一樣的道理。 建立一條Movie資料:
sleuth = Movie.objects.create(title='Sleuth', plot='an snobbish writer who loves games', year=1972, runtime=138,)
clear
Traceback (most recent call last):
File "<input>", line 1, in <module>
NameError: name 'clear' is not defined
sleuth.id
3
sleuth.get_rating_display()
'NR - 沒有評分'
複製程式碼
檢視資料庫表中是否有剛才建立的Movie物件; 可以在命令列中訪問資料庫表,也可以在pycharm的database視窗中視覺化檢視。
mysql> select * from core_movie;
+----+--------+------------------------------------+------+--------+---------+---------+
| id | title | plot | year | rating | runtime | website |
+----+--------+------------------------------------+------+--------+---------+---------+
| 1 | Sleuth | an snobbish writer who loves games | 1972 | 0 | 138 | |
+----+--------+------------------------------------+------+--------+---------+---------+
1 row in set (0.00 sec)
mysql>
複製程式碼
objects, 是模型model的預設manager。是一個查詢model表的介面。同時提供了一個**create()**方法來建立和儲存例項。每個model必須至少有一個manager,Django預設提供了一個manager。通常可以自定義manager,這個會在後續會詳述。
id,是資料庫表的主鍵,Django自動生成的。
get_rating_display(), 由Django生成,因為rating欄位提供了一個choices的元祖。我們在呼叫create()的時候沒有提供rating欄位值,因為rating欄位有預設值0.
get_rating_display()方法查詢並返回響應的value。Django將生成一個方法,遍歷擁有choices引數的所有欄位。
接下來用Django Admin app來建立一個後臺,來管理movies。
2.2.6 建立Movie的admin
快速生成一個後端UI,來快速填充專案資料。 為了讓Django的admin app使用我們新建的models,執行下列步驟:
1 註冊我們的model 2 建立一個superuser,可以訪問後臺 3 執行開發server 4 瀏覽器中訪問後臺
將Movie註冊到admin中, core/admin.py
from django.contrib import admin
from core.models import Movie
admin.site.register(Movie)
複製程式碼
註冊成功。 建立一個superuser hubery hubery2018
$ python manage.py createsuperuser
Username (leave blank to use 'hubery'): hubery
Email address: 934531487@qq.com
Password:
Password (again):
Superuser created successfully.
複製程式碼
執行開發server
$ python manage.py runserver
Performing system checks...
System check identified no issues (0 silenced).
July 19, 2018 - 05:35:58
Django version 2.0.6, using settings 'MyMovie.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
複製程式碼
瀏覽器中開啟提示的連結:
http://127.0.0.1:8000/
複製程式碼
會顯示出Django載入成功的頁面。
進入admin後臺頁: 瀏覽器中輸入:
http://127.0.0.1:8000/admin
複製程式碼
根據提示,輸入剛才建立的superuser資訊,成功登陸進去之後,可以可以線上編輯資料。
為了方便,我們可以趁熱打鐵的錄入一批movies。
2.2.7 建立MovieList檢視
當Django接收到一個請求request,它用request的路徑和專案的URLConf來匹配,找到找到在URLConf中配置的view並將該請求傳進去,這個view會返回一個HTTP response。
Django檢視View可以是函式,也可以是類。
FBVs Function-Based Views 基於函式的View CBVs Class-Based Views 基於類的View
我們寫一個檢視展示movie列表。
core/views.py
from django.http import HttpResponse
from django.views.generic import ListView
from core.models import Movie
# 基於class的檢視
class MovieList(ListView):
model = Movie
# 基於函式的檢視
def test(request):
return HttpResponse('hello world.')
複製程式碼
ListView至少需要一個model屬性。將會查詢model的所有行rows,將結果傳遞給template展示,在返回結果中渲染出template。同時提供了好多回撥函式,使得我們可以用來替換預設行為。
ListView是怎麼知道如何查詢Movie的所有物件的? 針對這個問題,我們得研究下manager和QuerySet類。每個model都有一個預設manager。Manager主要用來提供各種方法來查詢物件,如all(), 這些方法返回QuerySet。
QuerySet類是Django查詢資料庫的結果集,有很多方法,包含:**filter()**來限制查詢結果。QuerySet一個很好的特性是:它是惰性的,在我們從QuerySet獲取model之前,不會賦值。另一個不錯的功能是:例如filter()之類的方法,使用查詢表示式,這些表示式可以是欄位名稱,也可以跨越關係模型。
我們將會在整個專案中這樣處理。
注:所有manager類(objects)都有一個all()方法,返回一個QuerySet,
Movie.objects.all()
複製程式碼
相當於:
select * from core_movie;
複製程式碼
所以,ListView會檢查該ModelList檢視是否擁有model屬性,如果存在,它會知道Model類擁有一個預設manager(即objects),並且這個manager擁有all()方法。
2.2.8 新增用來展示movieList的模版template
模版引擎會搜尋project和app工程結構下的templates檔案目錄,根據檢視view來找相應的template;
ListView同時也提供了一個約定/規矩/慣例:template的存放目錄位置,我們自定義的模版必須遵守這個約定,如: <app_name>/<model_name>_list.html
core/movie_list.html
即:在core app中, app_name是core, model_name是Movie; 所以模版檔名為: <model_name>_list.html =>>> movie_list.html
movie_list.html模版的存放位置: core/templates/core/movie_list.html 這樣,模版引擎就能準確找到檢視對應的模版。
測試:
- 將movie_list.html的目錄變化一下, core/templates/core/movie_list.html 變成: core/templates/movie_list.html 重新整理瀏覽器就會報錯,在模版引擎中對應的正確位置上沒發現模版:
TemplateDoesNotExist at /movies
core/movie_list.html
複製程式碼
- 然後再將目錄還原: core/templates/movie_list.html 變成: core/templates/core/movie_list.html
所以, 切記
: 一定要嚴格遵守模版引擎的約定:正確存放自定義模版的位置,且:模版檔名也要按照約定來命名,兩個條件缺一不可。
依照慣例,模版的命名: core/movie_list.html
settings.py的配置檔案中,有一個預設的template目錄,將從這個目錄查詢模版。
如果不遵從這個慣例約定,可能就載入不出模版。
這個目錄可以被各個app覆蓋。
'DIRS': [os.path.join(BASE_DIR, 'templates')]
'APP_DIRS': True,
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',
],
},
},
]
複製程式碼
開始編碼template core/templates/core/movie_list.html 沒有相應目錄,就地建立
<!DOCTYPE html>
<html>
<body>
<ul>
{% for movie in object_list %}
<li>
{{ movie }}
</li>
{% empty %}
<li>
沒有電影列表。
</li>
{% endfor %}
</ul>
<p>
用的 https?
{{ request.is_secure|yesno }}
</p>
</body>
</html>
複製程式碼
Django的template是標準HTML,內建了變數variables和標籤tags。
內建引數 | 引數說明 |
---|---|
{{variables}} | variables等待被賦值的變數, 將指定變數的值插入到這裡 |
{% for item in item_list %} | 模版標籤 只要能讓模版系統做些事兒的 就是標籤 |
{{ ship_date|date: “F J, Y” }} | 過濾器類似Unix的管道,把ship_date傳遞給date過濾器,並給date過濾器指定“F J, Y”引數 |
其他特性用到了再細分討論,先忙正事兒。 [Django內建template和filters](docs.djangoproject.com/en/2.0/ref/…)
按照這個邏輯,模版中,{% extends '' %} {% block %} {% for movie in object_list %} {% url %} 是標籤; {{ movie }}是變數。
2.2.9 通過URLConf將請求定位到檢視View
接下來就是將檢視連結到URLConf上。
之前的篇幅中已經準備好了model,view和template,我們得利用URLConf告知Django哪個request應該被路由指向到Movielist檢視上。
什麼是URLConf?每個專案中都有個根URLConf,由Django建立的。
對於Django開發者來說,最好的辦法是每個app應用都有自己的URLConf。那麼接下來,根URLConf會通過**include()**函式 引入各個app目錄下的URLConf。
在core下建立一個URLConf, 即建立檔案:core/urls.py,
from django.urls import path
from core import views
app_name = 'core'
urlpatterns = [
# 基於class的檢視,必須呼叫View基類的靜態函式as_view()
# 返回一個可呼叫的字串 傳入path中
path('movies',
views.MovieList.as_view(),
name='MovieList',),
# 基於函式的檢視,可以直接將函式傳入url,可直接執行
url(r'$', views.test),
]
複製程式碼
最簡單的理解,一個URLConf就是一個擁有urlpatterns屬性的module,是一個path集合。
一個path由一個描述字串的字串組成,描述了有問題的和可呼叫的字串。
FBVs Function-Based Views 基於函式的View CBVs Class-Based Views 基於類的View
CBVs不可呼叫,所以View的基類用一個靜態函式**as_view()**返回一個可呼叫的字串;
FBVs可以作為回撥直接傳入,不需要**()操作符**,直接可以執行。
每個**path()**都應該起個名字,這樣對於當我們需要在template中引用這個path時很有用。
既然一個URLConf可以被其他URLConf引用include(),所以我們可能不知道view的全路徑。
Django提供了一個reverse()函式和url模版標籤,可以通過一個name找到一個view的全路徑。
app_name變數,設定了URLConf屬於哪個app。這樣,我們可以區分一個被命名的path,不至於多個app中有同名path的時候Django無法區分。類似於名稱空間
如,appA:index, appB:index, appC:index。
將core/urls.py連結到MyMovie/urls.py; 編輯MyMovie/urls.py
from django.contrib import admin
from django.urls import path, include
import core.urls
urlpatterns = [
path('admin/', admin.site.urls),
path('', include(core.urls, namespace='core')),
]
複製程式碼
2.2.10 執行開發server 檢視結果
cd MyMovie
python manage.py runserver
複製程式碼
瀏覽器中輸入:
http://127.0.0.1:8000/movies
複製程式碼
其中,基於函式的檢視直接返回了HttpResponse物件,可以在任何http測試工具中測試:
http://127.0.0.1:8000/test
複製程式碼
這個外界測試Http介面,後續會單獨介紹如何寫api。
2.3 建立Movie相關頁面
既然已經完成了專案的佈局,那我們可以加快進度。我們已經記錄了每個movie的資訊。我們來建立一個檢視,專門顯示具體的movie資訊。
我們需要做三件事:
2.3.1 建立MovieDetailView
類似Django提供的ListView,同時也提供了DetailView,來顯示單個model的詳細資訊。
core/views.py
from django.http import HttpResponse
from django.shortcuts import render
from django.views.generic import (
ListView, DetailView
)
from core.models import Movie
# 基於class的檢視
class MovieList(ListView):
model = Movie
# movie詳情 檢視
class MovieDetail(DetailView):
model = Movie
複製程式碼
DetailView需要一個path()物件,引入一個pk或者slug,因此DetailView可以向QuerySet傳遞引數來查詢特定的model例項。
一個slug是一個簡短的URL友好標籤,通常用於內容繁多的網站。
2.3.2 建立movie_detail.html模版
已經有了檢視view,那麼緊接著建立一個對應的模版。
Django的template支援複用,標記block部分,其他模版可以重寫該block部分。這樣就可以抽象出一個基類模版
,其他模版可以繼承該基類模版進行擴充。
基類模版, MyMovie/templates/base.html
目錄預設可能沒有,手動建立
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>
{% block title %} {% endblock %}
</title>
<style>
.mymdb-masthead {
background-color: #EEEEEE;
margin-bottom: 1em;
}
</style>
</head>
<body>
<div class="mymdb-masthead">
<div class="container">
<nav class="nav">
<div class="navbar-brand">MyMDB</div>
<a class="nav-link"
href="{% url 'core:MovieList' %}">
Movies
</a>
</nav>
</div>
</div>
<div class="container">
<div class="row">
<div class="clo-sm-8 mymdb-main">
{% block main %} {% endblock %}
</div>
<div class="col-sm-3 offset-sm-1 mymdb-sidebar">
{% block sidebar %}
{% endblock %}
</div>
</div>
</div>
</body>
</html>
複製程式碼
該base.html包含三塊block:title/main/sidebar
{%block %}: 告訴模版引擎,這部分內容可以被子模版覆蓋。
{% block title %}MyMovie{% endblock %}, 建立了一個title的block,其他templates可以替換該部分。如果其他模版沒替換,即沿用預設基類的。
href="{% url 'core:MovieList' %}" url標籤會生成一個URL的path,URL的名字應該這樣命名:<app_namespace>:, 比如:core是namespace,name是MovieList,那麼url標籤後面跟的就是core:MovieList。
建立一個簡單的模版 core/templates/core/movie_detail.html
{% extends 'base.html' %}
{% block title %}
{{ object.title }} - {{ block.super }}
{% endblock %}
{% block main %}
<h1>{{ object }}</h1>
<p class="lead">
{{ object.plot }}
</p>
{% endblock %}
{% block sidebar %}
<div>
這個電影排名:
<span class="badge badge-primary">
{{ object.get_rating_display }}
</span>
</div>
{% endblock %}
複製程式碼
這個模版只有很少的HTML部分,因為大部分HTML在base.html中已經包含了。movie_detail.html需要做的就是給base.html中定義的blocks提供數值。
看下新的標籤:
{% extends 'base.html' %} 用extends來實現繼承
另外一個模版。Django先找到base模版先執行,然後再替換blocks。
{% object.title %} - {% block.super %}
{% block.super %} 魔法變數,從base模版中的block中獲取內容,提供base模版中渲染後的文字。
{{ object.get_rating_display }} Django模版不用()來執行函式,直接用函式名字就可以執行。
2.3.3 將MovieDetail檢視配置到URLConf中
from django.conf.urls import url
from django.urls import path
from core import views
app_name = 'core'
urlpatterns = [
# 基於class的檢視,必須呼叫View基類的靜態函式as_view()
# 返回一個可呼叫的字串 傳入path中
path('movies',
views.MovieList.as_view(),
name='MovieList',),
# 向MovieDetail檢視中傳遞pk引數 獲取特定的movie物件
path('movie/<int:pk>',
views.MovieDetail.as_view()),
# 基於函式的檢視,可以直接將函式傳入url,可直接執行
url(r'$', views.test),
]
複製程式碼
瀏覽器中輸入:
http://127.0.0.1:8000/movie/2
複製程式碼
待截圖顯示
2.4 處理Movie列表分頁 點選跳轉
2.4.1 給MovieList.html增加點選跳轉到詳情頁功能
core/movie_list.html
{% extends 'base.html' %}
{% block title %}
所有Movies
{% endblock %}
{% block main %}
<ul>
{% for movie in object_list %}
<li>
<a href="{% url 'core:MovieDetail' pk=movie.id %}">
{{ movie }}
</a>
</li>
{% endfor %}
</ul>
{% endblock %}
複製程式碼
url標籤,用了一個叫pk(primary key)的引數,因為MovieDetail URL需要一個pk引數。如果沒有引數,渲染過程中Django會拋一個NoReverseMatch的異常,導致500錯誤。
重新整理瀏覽器
http://127.0.0.1:8000/movies
複製程式碼
會發現movies列表顯示的是超連結樣式,點選可以進入到詳情頁。 截圖:
2.4.2 設定排序
Model中新增一個內部類 Meta 指定ordering欄位
from django.db import models
# 編寫第一個model Movie
class Movie(models.Model):
#省略之前的部分
# Model內部類 可以指定Model的資訊。
class Meta:
ordering = ('-year', 'title')
def __str__(self):
return '{} ({})'.format(self.title, self.year)
複製程式碼
ordering欄位,指定排序參照的欄位,year降序,title。對應sql語句:
order by year desc, title;
複製程式碼
重新整理瀏覽器,可以發現,movie列表是降序排列。
2.4.3 新增分頁
分頁有點兒問題 程式碼先放上, 先跳過這部分
。
既然movies已經排序了,直接給加上分頁。Django的ListView檢視額外已經內建了分頁,所以直接用就好。
Pagination由GET引數控制page頁的顯示。
將分頁功能加到block main的底部;
{% extends 'base.html' %}
{% block title %}
所有Movies
{% endblock %}
{% block main %}
<ul>
{% for movie in object_list %}
<li>
<a href="{% url 'core:MovieDetail' pk=movie.id %}">
{{ movie }}
</a>
</li>
{% endfor %}
</ul>
{% comment 新增分頁 先註釋掉 %}{% if is_paginated %}
<nav>
<ul class="pagination">
<li class="page-item">
<a href="{% url 'core:MovieList' %}?page=1"
class="page-link">First</a>
</li>
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link"
href="{% url 'core:MovieList' %}?page={{ page_obj.previous_page_num}}">
{{ page_obj.previous_page_number }}
</a>
</li>
{% endif %}
<li class="page-item active">
<a href="{% url 'core:MovieList' %}?page={{ page_obj.number }}">
{{ page_obj.number }}
</a>
</li>
{% if page_obj.has_next %}
<li class="page-item">
<a href="{% url 'core:MovieList' %}?page={{ page_obj.next_page_number }}"
class="page-link">
{{ page_obj.next_page_number }}
</a>
</li>
{% endif %}
<li>
<a href="{% url 'core:MovieList' %}?page=last"
class="page-link">
Last
</a>
</li>
</ul>
</nav>
{% endif %}{% endcomment %}
{% endblock %}
複製程式碼
2.4.4 404錯誤
如果URL輸入錯誤,就會出現404,資源不存在錯誤;可以自定義404提示頁面,這個也無傷大雅,回頭補充。
2.4.5 測試檢視和模版
類似於java的junit測試,先不寫了。
2.5 新增Person和model關係
建模部分,涉及到外來鍵, 一對多
, 多對多
關係的梳理。
Django Web實戰-01建立專案-model 擴充套件
3 小結
綜上,你會發現,用Django的套路是:
- 配置專案屬性,settings.py
- 建立一個應用core
- 在core/models.py中編寫模型處理資料庫資料
- 再core/views.py中編寫檢視用來處理HTTP
- 在core/urls.py中配置應用路徑,使得檢視view可到達
- 再將core/urls.py連結到Django專案的urls.py中
- 如果需要展示檢視的http返回內容或者資料庫返回內容,藉助Django模版
Django用的是MVT模式
,本質就是MVC。
至於MVT各個模組之間怎麼銜接起來的,那就問Django了呵。開個玩笑,Django框架本身處理的非常完美,後續好好梳理下這個呼叫的流程。
宗旨:儘量不重複造輪子。
Django2 Web實戰01-啟動專案-model 擴充套件
關於原始碼,還在梳理整合,需要留言,回頭釋出到github上。
天星技術團QQ:557247785
。