1. 程式人生 > >Python自動化開發學習18-Django基礎篇

Python自動化開發學習18-Django基礎篇

python django

自定義Web框架

跟著老師一點一點完善代碼和文檔結構,最終一個Web框架的雛形會顯現出來,然後引出之後要學習完善的的Web框架。

Web框架的本質

Web服務端的本質就是一個socket服務端,而我們的瀏覽器就是socket客戶端。瀏覽器發送的請求就是一次socket請求。一次請求獲得一次響應,然後就斷開,這是一個短連接。
下面是一個服務端的python代碼,直接用socket,實現一個簡單的Hello World:

import socket

def handle_request(conn):
    data = conn.recv(1024)  # 接收數據,隨便收到啥我們都回復Hello World
    conn.send(‘HTTP/1.1 200 OK\r\n\r\n‘.encode(‘utf-8‘))  # 這是什麽暫時不需要知道,客戶端瀏覽器會處理
    conn.send(‘Hello World‘.encode(‘utf-8‘))  # 回復的內容,就是網頁的內容

def main():
    # 先起一個socket服務端
    server = socket.socket()
    server.bind((‘localhost‘, 8000))
    server.listen(5)
    # 然後持續監聽
    while True:
        conn, addr = server.accept()  # 開啟監聽
        handle_request(conn)  # 將連接傳遞給handle_request函數處理
        conn.close()  # 關閉連接

if __name__ == ‘__main__‘:
    main()

然後打開本機的瀏覽器訪問我們的Web服務。在地址欄輸入 http://127.0.0.1:8000/ 或 http://localhost:8000/ 之後,瀏覽器上就會顯示服務端返回的內容了。

WSGI

WSGI(Web Server Gateway Interface),是一種規範。它定義了使用python編寫的web app與web server之間接口格式,實現web app與web server間的解耦。
對我們來說,就是我們不用寫socket了,直接寫Web服務。
python標準庫提供的獨立WSGI服務的模塊稱為wsgiref。

from wsgiref.simple_server import make_server

def RunServer(environ, start_response):
    # environ:封裝了客戶端發來的所有的數據
    # start_response:封裝了要返回給用戶的數據,響應頭和響應狀態
    start_response(‘200 OK‘, [(‘Content-Type‘, ‘text/html‘)])
    # 返回給客戶端的內容,用return返回。記得要轉成bytes類型
    return [‘<h1>Hello, web!</h1>‘.encode(‘utf-8‘), ]

if __name__ == ‘__main__‘:
    httpd = make_server(‘‘, 8000, RunServer)  # 創建服務,不需要我們自己創建socket服務了
    print("Serving HTTP on port 8000...")
    httpd.serve_forever()  # 運行服務

響應不同的url請求

通常我們的請求可能要訪問的是網站中的某個頁面,比如是這樣的形式 http://localhost:8000/data 。這需要在服務端能夠區別出不同的請求,然後給客戶端返回不同的內容。
environ,封裝了客戶端發來的所有的數據,現在來找一下請求的數據具體在哪裏。
在上面例子中的 RunServer 函數中添加斷點,然後Debug模式啟動。此時再用上面的url發起請求。
技術分享圖片

如上圖,可以看到返回了兩個值。展開 environ 後,可以找到 PATH_INFO 內存放的就是 ‘/data‘ ,也就是我們請求的頁面。
修改一下上面的函數,在函數內判斷一下 environ[‘PATH_INFO‘] 的值,返回給客戶端不同的結果:

from wsgiref.simple_server import make_server

def handle_data():
    return [‘<h1>Hello, this is data.</h1>‘.encode(‘utf-8‘), ]

def handle_default():
    return [‘<h1>Hello, web!</h1>‘.encode(‘utf-8‘), ]

def RunServer(environ, start_response):
    start_response(‘200 OK‘, [(‘Content-Type‘, ‘text/html‘)])
    current_url = environ[‘PATH_INFO‘]
    if current_url == ‘/data‘:
        # 處理方法單獨寫一個函數
        return handle_data()
    else:
        # 寫一個默認的處理方法,比如也可以是返回404
        return handle_default()

if __name__ == ‘__main__‘:
    httpd = make_server(‘‘, 8000, RunServer)
    print("Serving HTTP on port 8000...")
    httpd.serve_forever()  # 運行服務

上面的結構還不是很好。如果請求的分支很多,這個if要寫的很長。這裏可以用字典的形式來存放,看著結構更加清楚。題外話,python沒有case語句,這也是一個變通的方式

from wsgiref.simple_server import make_server

# 這裏是返回頁面的處理函數
def handle_data():
    return [‘<h1>Hello, this is data.</h1>‘.encode(‘utf-8‘), ]

def handle_home():
    return [‘<h1>You are at home.</h1>‘.encode(‘utf-8‘), ]

def handle_default():
    return [‘<h1>Hello, web!</h1>‘.encode(‘utf-8‘), ]

# 寫一個字典,裏面是url和處理函數的對應關系
URL_DICT = {
    ‘/data‘: handle_data,
    ‘/home‘: handle_home,
    ‘default‘: handle_default
}

def RunServer(environ, start_response):
    start_response(‘200 OK‘, [(‘Content-Type‘, ‘text/html‘)])
    current_url = environ[‘PATH_INFO‘]
    if current_url in URL_DICT:
        func = URL_DICT[current_url]
    else:
        func = URL_DICT[‘default‘]
    if func:
        return func()
    else:
        return [‘<h1>404</h1>‘.encode(‘utf-8‘), ]

if __name__ == ‘__main__‘:
    httpd = make_server(‘‘, 8000, RunServer)
    print("Serving HTTP on port 8000...")
    httpd.serve_forever()  # 運行服務

上面的字典(URL_DICT)裏的key就是客戶端請求的url,例子中是明確的對應關系。再復雜一點key值寫成正則表達式,if再用正則匹配,就可以把一類請求匹配到一個處理函數中了。這麽沒繼續深入。

分離出html文件

上面的例子中,html的代碼是直接寫在handle方法裏的。實際操作中,這個html代碼會很長,並且我們學習Web前端的時候,已經編寫了完整的html頁面文件。這裏就需要把handle函數再改進一下,直接去讀取對應的文件然後返回文件內容:

def handle_request():
    with open("index.html", mode=‘rb‘) as file:
        data = file.read()
    return [data, ]

既然已經分離出文件了,那麽把所有html文件整理一下,統一存在在一個目錄下,比如:View。

分離出handle代碼

一個站點會有很多的頁面,所以對應的handle函數也會有很多個。把所有的handle函數也單獨拿出來,寫到一個或幾個模塊中去。把這些模塊也放到一個目錄下,比如:Controller/account.py。用的時候導入模塊,修改一下字典的函數名:

from Contraller import account

# 寫一個字典,裏面是url和處理函數的對應關系
URL_DICT = {
    ‘/data‘: account.handle_data,
    ‘/home‘: account.handle_home,
    ‘default‘: account.handle_default
}

返回動態數據

之前例子中返回客戶端的都是固定的html,現在要動態的返回數據。一般數據是從數據庫獲取的,這裏直接返回系統時間吧。修改html文件,加入一些特殊的標記,比如:“@time”。那麽現在我們的 View/index.html 文件內容如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>Hellow Web! It is @time.</h1>
</body>
</html>

然後修改我們的handle方法來處理這個特殊的標記:

import time

def handle_default():
    with open(‘View/index.html‘, mode=‘rb‘) as file:
        data = file.read()
        data = data.replace(b‘@time‘, time.asctime().encode(‘utf-8‘))
    return [data, ]

這裏寫死了一個@time的標記,實際使用中多數數據都是是讀取數據庫的,並且要讀的東西也很多。獲取動態數據的方法也要單獨分離出來寫成模塊。並且也建一個專門的文件夾存放,比如:Model。

MVC框架

現在我們已經有了這樣一個文檔結構了:

web_server.py
├─Model
│  └─get_data.py
├─View
│  └─index.html
└─Contraller
    └─account.py

於是一個MVC(Model View Controller 模型-視圖-控制器)的Web架構的模式就出來了。

MTV框架

另外還有一種,MTV(Model Templates View 模型-模板-視圖)。也就是把MVC中的View分成了視圖(展現哪些數據)和模板(如何展現)2個部分,而Contorller這個要素由框架自己來實現了,我們需要做的就是把(帶正則表達式的)URL對應到視圖就可以了,通過這樣的URL配置,系統將一個請求發送到一個合適的視圖。
在Python的世界中,基本(除了Pylons)都使用了MTV框架,包括Django。所以後面會重點學習Django。
另外,Flask這種微框架就不是一個MVC模式的,因為它沒有提供Model,除非集成了SQLAlchemy之類的ORM進來。

Django基礎

Python的WEB框架有 Django、Tornado、Flask 等多種,Django 相較與其他WEB框架其優勢為:大而全,框架本身集成了ORM、模型綁定、模板引擎、緩存、Session等諸多功能。

安裝

可以用pip3安裝:

pip3 install django

或者也可以通過PyCharm來安裝:File-->Settings-->Project 。
安裝完後,看一下Scripts目錄,也就是你的pip3程序所在的目錄,多了2個django-admin程序。這個是幫我創建Django程序的。

創建Django項目

切換到你要創建Django項目的目錄,執行django-admin程序,就可以完成創建:

django-admin.exe startproject [你的項目名稱]

你的確認把Script加到環境變量裏去了,否則得輸入完整的路徑,比如下面這樣:

G:\PycharmProjects>D:\Python36\Scripts\django-admin.exe startproject mysite01

現在進入你的項目目錄,啟動你的項目,命令如下。默認參數是 127.0.0.1:8000 ,可以輸入方框號中完整的IP和端口號來指定,比如:0.0.0.0:9001。

python manage.py runserver [127.0.0.1:8000]

然後可以打開瀏覽器訪問你的測試頁了,能成功訪問則說明上面的步驟都正確了。
上面的步驟也可以通過PyCharm來完成,如果是照著上面用命令行來創建的,現在還需要把我們的項目加到PyCharm裏面去。File-->Open 然後選擇你項目的目錄,如果按上面的例子那麽就是 G:\PycharmProjects\mysite01 ,點OK完成。
直接通過PyCharm創建項目:文件-->新建項目,選擇Django
Location 裏修改一下項目的路徑和項目名,默認項目名是untitled,改掉。
Project Interpreter 裏選擇python環境,可以新建一個環境,或者使用已有的環境。
More Settings 裏默認會幫我們創建templates文件夾。如果需要,還可以把 Application name 填上,就是創建項目的同時創建一個app。
templates 和 app 在下面的 “##創建app“ 和 “##寫一個登錄頁面” 裏會講
技術分享圖片

創建一個頁面

在項目下新建一個文件夾存放我們的頁面,文件夾下新建一個python程序如下:

from django.shortcuts import HttpResponse

def hello(request):
    return HttpResponse("Hello Web")

現在去修改一下原來urls.py文件,加入上面這個頁面的對應關系:

from django.contrib import admin
from django.urls import path

from page import mypage  # 導入剛才寫的程序

urlpatterns = [
    path(‘admin/‘, admin.site.urls),
    path(‘hello/‘, mypage.hello),  # 添加我們的對應關系
]

添加2行代碼,導入py文件,添加對應關系。現在再去重啟一個Web服務。直接訪問這次頁面會返回 Page not found (404) 。在url後加上 hello 能返回程序中 return 的字符串。

創建app

通過創建app,相當於在網站下創建了一個相對獨立的功能模塊。我們可以創建多個app,為網站建立多個功能模塊。創建的方法,是在命令行下輸入命令:

python manage.py startapp [你的app的名字]

創建完之後,你的項目裏又多了一個文件夾(app的名字),以及一些文件。其中views是給我放置處理函數的,把前面寫的處理函數移動到這個文件裏。然後再去 urls.py 裏修改一下。
創建一個app,比如名字就叫cmdb:

python manage.py startapp cmdb

在 cmdb/views.py文件的後面追加我們的處理函數:

from django.shortcuts import render

# Create your views here.
# 上面是原來文件的內容,在下面添加我們的處理函數
from django.shortcuts import HttpResponse

def hello(request):
    return HttpResponse("Hello Web")

def home(request):
    return HttpResponse("<h1>Hello here is home</h1>")

修改urls.py裏的對應關系:

from django.contrib import admin
from django.urls import path

from cmdb import views

urlpatterns = [
    path(‘admin/‘, admin.site.urls),
    path(‘home/‘, views.home),
    path(‘hello/‘, views.hello),
]

功能文件介紹

templates文件夾 :默認不會創建這個文件夾,不過使用PyCharm創建項目的時候會為我們創建它。存放我們的html文件。
setting.py :配置文件
url.py :url的對應關系
wsgi.py :遵循WSGI規範,默認用的是wsgiref。老師推薦上線的系統用 uwsgi 替換掉默認的,也是這裏改。推薦 uwsgi + nginx 。
manage.py :管理Django程序。有很多功能通過命令行參數控制,比如前面用的 runserver 和 startapp。
app下的文件
migrations文件夾 :存放數據庫操作的記錄,是修改表結構的記錄,不包括數據修改的記錄。我們可以不用管這個文件夾裏的內容
admin.py :Django提供的後臺管理
apps.py :配置當前app的
models.py :創建數據庫表結構,就是我們的ORM
tests.py :單元測試
views.py :業務代碼
文件結構如下:

{project}
├─{app}
│  ├─migrations
│  │  └─__init__.py
│  │
│  ├─__init__.py
│  ├─admin.py
│  ├─apps.py
│  ├─models.py
│  ├─tests.py
│  └─views.py
│
├─{project}
│  ├─__init__.py
│  ├─settings.py
│  ├─urls.py
│  └─wsgi.py
│
├─templates
│  └─index.html  # 這裏放我們自己寫的頁面
│
└─manage.py

{project} 指代項目名,{app} 指代app名。之後寫文件路徑路徑的時候,也可能這麽寫

寫一個登錄頁面

在app裏創建頁面,views.py 裏不再返回字符串,而是去讀取html文件。
創建html頁面文件,templates/login.html :

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        label{
            width: 80px;
            text-align: right;
            display: inline-block;
        }
    </style>
</head>
<body>
<!--
這裏註意"/login/"結尾的"/"。瀏覽器中我們不輸最後一個"/",但是Django默認會幫我們加上
但是代碼裏的,不會自動添加。加不加最後的"/",對程序而言就是兩個url。
我們在urls.py裏寫的是帶"/"的,寫不對會導致找不到url。
不過Django會判斷出來是因為你的url最後沒加"/",會提示你去settings.py裏加一條APPEND_SLASH=False。
人家默認開啟是有意義的,我們需要做的就是別忘記在url最後寫上各個"/"
-->
<form action="/login/" method="post">
    <p>
        <label for="usernmae">用戶名:</label>
        <input id="usernmae" type="text" />
    </p>
    <p>
        <label for="password">密碼:</label>
        <input id="password" type="password" />
        <input type="submit" value="提交">
    </p>
</form>
</body>
</html>

{app}/views.py裏寫一個login方法:

from django.shortcuts import HttpResponse
def login(request):
    with open(‘templates/login.html‘, encoding=‘utf-8‘) as file:
        data = file.read()
    return HttpResponse(data)

記得修改一下{project}/urls.py添加對應關系,現在可以打開新頁面看一下了。
一般都是打開文件返回文件內容,所以django給我們提供了一個 render 方法,直接返回文件內容,login方法改成下面這樣:

from django.shortcuts import render  # 這行默認在文件第一行就自動幫我引入了,沒有你就加上
 def login(request):
    # 現在可以直接讀取文件返回文件內容了,一行搞定
    return render(request, ‘login.html‘)

這裏的文件直接寫文件名就好了,不過現在測試的話,回顯示 TemplateDoesNotExist at /login/ ,因為系統找不到文件。再去設置一下 {project}/settings.py 裏的變量 TEMPLATES 。裏面有個字典,設置前是這樣的:

TEMPLATES = [
    {
        ‘BACKEND‘: ‘django.template.backends.django.DjangoTemplates‘,
        ‘DIRS‘: [],  # 這裏加上我們的查找路徑
        ‘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‘,
            ],
        },
    },
]

DIRS的暫時是個空列表,加上templates的絕對目錄 ‘DIRS‘: [os.path.join(BASE_DIR, ‘templates‘)] 。這樣就可以直接通過文件名找到 templates 目錄下的文件了。

支持引入靜態文件

網頁中還需要引入css模板,jQuery文件等,現在嘗試把這些加上。
建立一個文件夾 “static” 來存放這些靜態文件。然後創建一個css文件 “commons.cc” 給body加一個背景色:

body{
    background-color: grey;
}

記得在頁面的 head 裏引入:

<link rel="stylesheet" href="/static/commons.css">

以默認的配置,系統是找不到我們的靜態文件的,還需要在配置文件 settings.py 裏條件配置:

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.0/howto/static-files/

STATIC_URL = ‘/static/‘
# 上面都是原來就有的內容,下面的STATICFILES_DIRS是我們要添加的
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, ‘static‘),
)

現在,就可以支持引入靜態文件了,包括jQuery以及前端組件現在都能用了。
課上暫時沒講。上面的引入方式,還是有問題。現在通過Django是可以全部訪問了,但是本地打開html文件的時候是找不到引入的文件的。把路徑的前面加上 ".." 表示上一級目錄,就可以兩邊兼顧了:

<link rel="stylesheet" href="../static/commons.css">

前端小結

總結一下上面的步驟
PyCharm創建項目:
如果用Pycharm創建,修改一下項目名稱,確認是新建python環境還是用已有的。可以選擇同時創建app。
命令行創建項目:
創建項目 django-admin.exe startproject [你的項目名稱]
創建app python manage.py startapp [你的app的名字]
頁面文件html:
把html文件放到templates文件夾下,PyCharm創建的時候會自動創建這個文件
靜態文件:
創建一個static文件夾,放置我們的靜態文件。html文件中類似這樣引入

<link rel="stylesheet" href="../static/commons.css">

添加templates的文件夾路徑:
PyCharm創建的項目會自動給我們加上,如果是用命令行創建的話,需要自己修改。
settings.py 文件的 TEMPLATES 變量 ‘DIRS‘: [os.path.join(BASE_DIR, ‘templates‘)]
添加static的文件夾路徑:
settings.py 文件的最後加上下面這些

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, ‘static‘),
)

編輯處理函數 {app}/views.py :

from django.shortcuts import render  # 這行默認就在文件第一行

def login(request):
    return render(request, ‘index.html‘)

添加對應關系:
修改urls.py的內容,添加對應關系

Django請求

下面以一個表單的示例來說明前端和後端之間的通信,提交和獲取。

提交表單

現在頁面已經可以打開了。然後看一下我們的form表單,form標簽內的內容如下:

<form action="/login/" method="post">

action :是我們要提交表單的地址,這裏要註意最後的"/",平時瀏覽器上我們有沒有無所謂,系統會自動補上的,代碼裏就不能省了
method :是提交表單的方式,有post和get兩種。我們瀏覽器輸入地址訪問也是get
現在用post提交會有問題,可以先換成get,甚至把action也改一下,看下效果:

<form action="/admin/" method="get">

但是換回post就是有問題,提示CSRF(跨站請求偽造)。先去settings.py裏把這個設置關掉,如下:

MIDDLEWARE = [
    ‘django.middleware.security.SecurityMiddleware‘,
    ‘django.contrib.sessions.middleware.SessionMiddleware‘,
    ‘django.middleware.common.CommonMiddleware‘,
    # ‘django.middleware.csrf.CsrfViewMiddleware‘,
    ‘django.contrib.auth.middleware.AuthenticationMiddleware‘,
    ‘django.contrib.messages.middleware.MessageMiddleware‘,
    ‘django.middleware.clickjacking.XFrameOptionsMiddleware‘,
]

現在再提交就不會報CSRF了,由於提交的地址就是當前頁面,所以目前能看到的效果只是頁面的刷新。
通過F12進入開發者模式,看網絡標簽,提交頁面後會刷出請求的服務器資源。右邊可以展開顯示詳細的信息。可以看到請求URL、請求方法。如果你是get方法,請求方法就是GET,如果你是post方法,請求方法就是POST。下面還有請求頭、響應頭。
請求頭裏有個內容知道一下,你的系統和瀏覽器信息會在這裏,如果是手機,這裏也能看到手機的系統

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299

修改一下我們的views.py文件中的方法,也能獲取到用戶請求的方法:

def login(request):
    # request 包含用戶提交的所有信息
    # request.method 獲取用戶提交的方法
    print(request.method)
    return render(request, ‘login.html‘)

request :包含用戶提交的所有信息
request.method : 獲取用戶提交的方式 get 或 post
現在我們可以通過request.method判斷出收到的是get請求還是post請求。如果是get就正常返回頁面,如果是post就處理用戶表單。
為了能獲取到post的信息,還需要為input加上name屬性。之前的input未設置name,現在需要加上:

<form action="/login/" method="post">
    <p>
        <label for="usernmae">用戶名:</label>
        <input id="usernmae" name="user" type="text" />
    </p>
    <p>
        <label for="password">密碼:</label>
        <input id="password" name="pwd" type="password" />
        <input type="submit" value="提交">
    </p>
</form>

解決了上面的問題,終於可以著手處理瀏覽器提交的信息了。
request.POST 就是用戶提交的表單信息。可以把這個當成是一個字典來操作。上面已經為input加上的name屬性,這個就是字典的key。推薦用字典的get方法來獲取值,因為如果獲取不到,默認是返回None不會報錯。獲取到用戶名和密碼後簡單驗證一下,然後如果正確就跳轉到另外一個頁面。頁面跳轉要用到 redirect 函數,還得在開頭導入模塊 from django.shortcuts import redirect ,然後login處理函數如下:

from django.shortcuts import render
from django.shortcuts import redirect

def login(request):
    # request 包含用戶提交的所有信息
    # request.method 獲取用戶提交的方法
    # print(request.method)
    if request.method == ‘POST‘:
        # 這裏用字典的操作方法,用get方法獲取值
        user = request.POST.get(‘user‘)
        pwd = request.POST.get(‘pwd‘)
        # print(user, pwd)
        if user == ‘steed‘ and pwd == ‘123‘:
            return redirect(‘http://blog.51cto.com/steed‘)
    return render(request, ‘login.html‘)

redirect() 裏面只能填url,就上例子中那樣。如果需要跳轉本站的地址,也可以,必須以 / 開頭,這裏開頭的 / 就代指本機的地址,如:/login 就是一個本機的url。

返回數據

通過Django給客戶端返回動態的數據,需要用到模板自己的語言,該語言可以實現數據展示。這裏要用到模板語言中的變量
變量如下所示:{{ item }}。 當模板引擎遇到變量時,它將評估該變量並將其替換為結果。
這樣在html中遇到用兩個大括號包起來的形式,Django會把它替換成我們後端指定的內容。現在我們來輸出錯誤信息,繼續修改我們的login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="../static/commons.css">
    <style>
        label{
            width: 80px;
            text-align: right;
            display: inline-block;
        }
    </style>
</head>
<body>
<form action="/login/" method="post">
    <p>
        <label for="usernmae">用戶名:</label>
        <input id="usernmae" name="user" type="text" />
    </p>
    <p>
        <label for="password">密碼:</label>
        <input id="password" name="pwd" type="password" />
        <input type="submit" value="提交">
        <span style="color: red">{{ error_msg }}</span>
    </p>
</form>
</body>
</html>

上面是完整的文件內容,這次是在最後加了一個span標簽,裏面用來返回錯誤信息,錯誤信息用{{ error_msg }}代替。
然後繼續去編輯之前的login處理函數,之前還沒寫認證失敗的邏輯。現在加上並且替換到頁面裏的錯誤信息。這裏還是用到之前的render方法。這個方法還有第三個參數,傳入一個字典,key就是頁面中需要替換的內容,value是我們要替換上去的內容。具體如下:

from django.shortcuts import render
from django.shortcuts import redirect

def login(request):
    # request 包含用戶提交的所有信息
    # request.method 獲取用戶提交的方法
    # print(request.method)
    if request.method == ‘POST‘:
        # 這裏用字典的操作方法,用get方法獲取值
        user = request.POST.get(‘user‘)
        pwd = request.POST.get(‘pwd‘)
        # print(user, pwd)
        if user == ‘steed‘ and pwd == ‘123‘:
            return redirect(‘http://blog.51cto.com/steed‘)
        if user and user != ‘steed‘:
            return render(request, ‘login.html‘, {‘error_msg‘: ‘用戶名%s不存在‘ % user})
    return render(request, ‘login.html‘)

get方法提交的時候,返回的值是沒有寫第三個參數的,頁面裏也看不到錯誤信息。這裏Django也幫我麽把要替換的內容默認設為空了。如果輸入一個錯誤的用戶名,這裏也動態的把這個用戶名返回到頁面上了。
使用點(.)來訪問變量的屬性:
從技術上講,當模板系統遇到點時,會按以下順序嘗試以下查找:

  • 字典查找
  • 屬性或方法查找
  • 數字索引查找

所以還可以用點來訪問到字典或列表中的元素。

返回表格數據-for標簽

Django的模板語言還支持循環,這樣我們可以方便的獲取一系列的數據,比如字典和列表。模板語句中叫做標簽
標簽看起來像這樣:{% tag %}。 一些標簽需要開始和結束標簽(即 {% tag %} ... 標簽 內容 ... {% endtag %})。現在先重點來看一下for標簽,循環數組中的每個項目。 例如,要顯示athlete_list中提供的運動員列表:

<ul>
{% for item in item_list %}
    <li>{{ item.key }}</li>
{% endfor %}
</ul>

按照上面的格式寫好我們的html文件:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div>
    <table border="1">
        <thead>
        <tr>
            <th>NAME</th>
            <th>AGE</th>
            <th>DEPT.</th>
        </tr>
        </thead>
        <tbody>
        <!--
        <tr>
            <td>Adam</td>
            <td>22</td>
            <td>IT</td>
        </tr>
        <tr>
            <td>Bob</td>
            <td>32</td>
            <td>IT</td>
        </tr>
        <tr>
            <td>Carmen</td>
            <td>30</td>
            <td>Sales</td>
        </tr>
        <tr>
            <td>David</td>
            <td>40</td>
            <td>HR</td>
        </tr>
        <tr>
            <td>Edda</td>
            <td>26</td>
            <td>HR</td>
        </tr>
        -->
        {% for row in members %}
        <tr>
            <td>{{ row.name }}</td>
            <td>{{ row.age }}</td>
            <td>{{ row.dept }}</td>
        </tr>
        {% endfor %}

        </tbody>
    </table>
</div>
</body>
</html>

表格的內容用html也寫了一份,但是註釋掉了。這裏的標簽使用的變量是 members ,然後標簽內部是對members做遍歷,一次取出name、age、dept。這是類似字典的操作。members是個列表,列表的成員是字典並且有nage、age、dept這些key。
現在要去寫我們的處理函數,把表格的數據寫進去。首先我們要有一份表格的數據,一般是去數據庫裏讀取的。暫時先不做數據庫操作,寫一個列表(TAB_MEMBER)存放表格數據。然後同樣是通過render來返回。這次把整個字典傳進去。views.py 文件:

# 現在也不搞數據庫,先創建這麽一個列表來存數據
TAB_MEMBER = [
    {‘name‘: ‘Adam‘, ‘age‘: 22, ‘dept‘: ‘IT‘},
    {‘name‘: ‘Bob‘, ‘age‘: 32, ‘dept‘: ‘IT‘},
    {‘name‘: ‘Carmen‘, ‘age‘: 30, ‘dept‘: ‘Sales‘},
    {‘name‘: ‘David‘, ‘age‘: 40, ‘dept‘: ‘HR‘},
    {‘name‘: ‘Edda‘, ‘age‘: 26, ‘dept‘: ‘HR‘},
]

def table(request):
    return render(request, ‘table.html‘, {‘members‘: TAB_MEMBER})

現在可以去重啟Web服務,打開新的頁面,檢查是否可以獲取到表格的數據顯示出來。

為表格添加數據

接下來為上面的表格添加數據,修改html文件的內容,在表格上面加上輸入框填寫數據以及一個添加按鈕:

<div>
    <form action="/table/" method="post">
        <input type="text" name="name" placeholder="NAME" />
        <input type="text" name="age" placeholder="AGE" />
        <input type="text" name="dept" placeholder="DEPT." />
        <input type="submit" value="添加" />
    </form>
</div>
<!-- 在表格的上面添加這個div,下面是原來表格的代碼 -->

通過上面的添加按鈕,會把添加的數據通過post請求發送到Web服務端,最後會返回一個更新後的表格。
現在要修改處理函數,添加對post請求的處理:

def table(request):
    if request.method == ‘POST‘:
        name = request.POST.get(‘name‘)
        age = request.POST.get(‘age‘)
        dept = request.POST.get(‘dept‘)
        tmp = {‘name‘: name, ‘age‘: age, ‘dept‘: dept}
        TAB_MEMBER.append(tmp)
    return render(request, ‘table.html‘, {‘members‘: TAB_MEMBER})

最後都是返回列表的數據來生成表格,上面的做法是把收到的添加請求的內容生成一個新列表成員(字典)添加到 TAB_MEMBER 列表的末尾。
使用GET請求,上面用的是POST請求,也可以改成GET請求。把html中的form標簽的methon的值改成get,就是發送get請求了。而處理函數可以用 request.method == ‘GET‘ 做條件判斷,取值也和POST的處理方法一樣,使用 request.GET.get() 來獲取請求的數據。對處理函數修改一下可以同時處理 get 和 post 請求:

def table(request):
    if request.method == ‘POST‘:
        name = request.POST.get(‘name‘)
        age = request.POST.get(‘age‘)
        dept = request.POST.get(‘dept‘)
        tmp = {‘name‘: name, ‘age‘: age, ‘dept‘: dept}
        TAB_MEMBER.append(tmp)
    elif request.method == ‘GET‘  and request.GET:
        name = request.GET.get(‘name‘)
        age = request.GET.get(‘age‘)
        dept = request.GET.get(‘dept‘)
        tmp = {‘name‘: name, ‘age‘: age, ‘dept‘: dept}
        TAB_MEMBER.append(tmp)
    return render(request, ‘table.html‘, {‘members‘: TAB_MEMBER})

get請求也可以直接通過瀏覽器的地址欄發出。在url的後面加問號(?),然後拼接上請求的內容:

http://127.0.0.1:8000/table/?name=Frank&age=37&dept=Marketing

url和請求之間用 ? 分隔,字段之間用 & 分隔,字段左邊是 key 右邊是 value 中間是 = 。

選擇性的返回數據-if標簽

if標簽的語法如下:

{% if 條件1 %}
    當條件1為 true 時的代碼
{% elif 條件2 %}
    當條件2為 true 時的代碼
{% else %}
    所有條件都不為 true 時的代碼
{% endif %}

在上面的代碼的基礎上,修改一下html中的內容,在上面for循環裏的加一層if,比如只顯示age>30的條目:

<div>
    <table border="1">
        <thead>
        <tr>
            <th>NAME</th>
            <th>AGE</th>
            <th>DEPT.</th>
        </tr>
        </thead>
        <tbody>
        {% for row in members %}
        <tr>
            {% if row.age > 30 %}
            <td>{{ row.name }}</td>
            <td>{{ row.age }}</td>
            <td>{{ row.dept }}</td>
            {% endif %}
        </tr>
        {% endfor %}
        </tbody>
    </table>
</div>

Djang請求的生命周期

下面是一個請求的整個流程,括號裏是我們的代碼存放的位置
請求 <--> 路由系統(urls.py) <--> 視圖函數(views.py) <--> DB、html(templates)、靜態文件(static)

課後練習

主機管理:

  • 後臺數據使用數據庫,PyMySql 或者 SQLAlchemy 操作。
  • 一張主機管理數據表,至少8列。比如:主機名、IP地址、端口號、描述、狀態等
  • 一張用戶登錄認證表,有用戶名和密碼。不要求操作和管理,就是用來登錄驗證的
  • 一個登錄頁面,驗證通過跳轉到下一個頁面
  • 一個主機管理頁面,可以用模板也可以自己寫。至少3部分:頭部內容、左側菜單、中間顯示我們的數據表格
    • 查看所有的主機信息,信息只需要顯示部分數據,4列數據即可。其余的不用顯示出來,用下面的查看詳細顯示出來
    • 可以添加主機,就是添加一行數據,用模態對話框
    • 查看詳細,點擊打開一個新頁面,顯示當前主機的所有信息
    • 刪除主機,就是刪除一行

Python自動化開發學習18-Django基礎篇