Django2 web實戰01-啓動項目

做者:Hubery 時間:2018.7.24html

0 前言 = 瞎說

能寫web的語言有好多。python算是難度較低,入門較快的腳本語言。Django是python的web框架,詳情很少敘,見:java

Django-wiki介紹python

# 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 儘可能作到
不擋路,容許你在必要時脫離框架。 
複製代碼

寫項目就比如騎自行車,看完上面的背景,接下來就上代碼體驗了,直接開擼。mysql

按照草擬的項目結構,大體上有5塊內容:linux

  • 啓動項目
  • 新增用戶
  • 文件安全
  • 緩存列表
  • 項目部署

00.項目結構圖pro.jpg

項目初步規劃要完成一個電影列表/詳情的查看,評論,投票,得分,支持文件上傳,內容安全, 註冊/登錄/登出,內容緩存等。git

大體構思了下,要寫好仍是須要處理很多細節問題的。好比了解下Django的編碼套路,處理關聯關係,數據庫的CRUD等,一步步的來吧。程序員

本文主要實現項目的第一部分,啓動項目部分。github

1 環境準備

  • 安裝python 默認的就是最新的3.6.*

不一樣平臺安裝方式可能不一樣,自行百度一個。  www.python.org/downloads/web

  • 安裝django 默認的是最新的2.*
pip:pip install packages縮寫,是python的包管理工具,用於安裝python包。
複製代碼
pip install django // 安裝最新版
pip install django==2.*.* // 安裝具體版本
複製代碼
  • 安裝pycharm IDE

從網上下載一個就好了,而後搜一個激活碼完事兒。sql

  • 安裝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加載成功的頁面。

01.瀏覽器查看開發server.jpg

進入admin後臺頁: 瀏覽器中輸入:

http://127.0.0.1:8000/admin
複製代碼

根據提示,輸入剛纔建立的superuser信息,成功登錄進去以後,能夠能夠在線編輯數據。

02.admin後臺頁面能夠可視化的編輯內容.png

爲了方便,咱們能夠趁熱打鐵的錄入一批movies。

03.admin後臺手動錄入數據.jpg

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的全部對象的? 針對這個問題,咱們得研究下managerQuerySet類。每一個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 這樣,模版引擎就能準確找到視圖對應的模版。

測試:

  1. 將movie_list.html的目錄變化一下, core/templates/core/movie_list.html  變成: core/templates/movie_list.html 刷新瀏覽器就會報錯,在模版引擎中對應的正確位置上沒發現模版:
TemplateDoesNotExist at /movies
core/movie_list.html
複製代碼
  1. 而後再將目錄還原: core/templates/movie_list.html 變成: core/templates/core/movie_list.html

08自定義模版的存放位置.png

因此, 切記必定要嚴格遵照模版引擎的約定:正確存放自定義模版的位置,且:模版文件名也要按照約定來命名,兩個條件缺一不可。 依照慣例,模版的命名: 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),
]
複製代碼

05.視圖屬性不一樣註冊方式不一樣.jpg

最簡單的理解,一個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

歡迎來擾
相關文章
相關標籤/搜索