Python全棧開發-web框架之django

一:web框架html

什麼是web框架?java

Web應用框架(Web application framework)是一種開發框架,用來支持動態網站、網絡應用程序及網絡服務的開發。
這種框架有助於減輕網頁開發時共通性活動的工做負荷,例如許多框架提供數據庫訪問接口、標準樣板以及會話管理等,
可提高代碼的可再用性。

web應用的流程:python

//瀏覽器發送一個HTTP請求;

//服務器收到請求,生成一個HTML文檔;

//服務器把HTML文檔做爲HTTP響應的Body發送給瀏覽器;

//瀏覽器收到HTTP響應,從HTTP Body取出HTML文檔並顯示。
View Code

其實相對於全部的web應用,我感受就是客戶端與服務端之間的交互過程,瀏覽器就是一個socket客戶端。git

客戶端與服務端
因此,最簡單的Web應用就是先把HTML用文件保存好,用一個現成的HTTP服務器軟件,接收用戶請求,從文件中讀取HTML,返回。Apache、Nginx、Lighttpd等這些常見的靜態服務器就是
幹這件事情的。
若是要動態生成HTML,就須要把上述步驟本身來實現。不過,接受HTTP請求、解析HTTP請求、發送HTTP響應都是苦力活,若是咱們本身來寫這些底層代碼,還沒開始寫動態HTML呢,
就得花個把月去讀HTTP規範。
正確的作法是底層代碼由專門的服務器軟件實現,咱們用Python專一於生成HTML文檔。由於咱們不但願接觸到TCP鏈接、HTTP原始請求和響應格式,
因此,須要一個統一的接口,讓咱們專心用Python編寫Web業務。這個接口就是WSGI:Web Server Gateway Interface。
WSGI接口定義很是簡單,它只要求Web開發者實現一個函數,就能夠響應HTTP請求。
咱們來看一個最簡單的Web版本的「Hello, web!」:
from wsgiref.simple_server import make_server


def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return [b'<h1>Hello, web!</h1>']


httpd = make_server('', 8080, application)

print('Serving HTTP on port 8000...')
# 開始監聽HTTP請求:
httpd.serve_forever()
小程序
上面的application()函數就是符合WSGI標準的一個HTTP處理函數,它接收兩個參數:

environ:一個包含全部HTTP請求信息的dict對象;

start_response:一個發送HTTP響應的函數。

在application()函數中,調用:

start_response('200 OK', [('Content-Type', 'text/html')])
就發送了HTTP響應的Header,注意Header只能發送一次,也就是隻能調用一次start_response()函數。start_response()函數接收兩個參數,一個是HTTP響應碼,一個是一組list表示的HTTP Header,每一個Header用一個包含兩個str的tuple表示。
一般狀況下,都應該把Content-Type頭髮送給瀏覽器。其餘不少經常使用的HTTP Header也應該發送。

而後,函數的返回值'<h1>Hello, web!</h1>'將做爲HTTP響應的Body發送給瀏覽器。

有了WSGI,咱們關心的就是如何從environ這個dict對象拿到HTTP請求信息,而後構造HTML,經過start_response()發送Header,最後返回Body。

整個application()函數自己沒有涉及到任何解析HTTP的部分,也就是說,底層代碼不須要咱們本身編寫,咱們只負責在更高層次上考慮如何響應請求就能夠了。

不過,等等,這個application()函數怎麼調用?若是咱們本身調用,兩個參數environ和start_response咱們無法提供,返回的str也無法發給瀏覽器。

因此application()函數必須由WSGI服務器來調用。有不少符合WSGI規範的服務器,咱們能夠挑選一個來用。可是如今,咱們只想儘快測試一下咱們編寫的application()函數真的能夠把HTML輸出到瀏覽器,因此,要趕忙找一個最簡單的WSGI服務器,把咱們的Web應用程序跑起來。
好消息是Python內置了一個WSGI服務器,這個模塊叫wsgiref,它是用純Python編寫的WSGI服務器的參考實現。所謂「參考實現」是指該實現徹底符合WSGI標準,可是不考慮任何運行效率,僅供開發和測試使用。
attention
運行WSGI服務

咱們先編寫hello.py,實現Web應用程序的WSGI處理函數:

# hello.py

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return '<h1>Hello, web!</h1>'
而後,再編寫一個server.py,負責啓動WSGI服務器,加載application()函數:

# server.py
# 從wsgiref模塊導入:
from wsgiref.simple_server import make_server
# 導入咱們本身編寫的application函數:
from hello import application

# 建立一個服務器,IP地址爲空,端口是8000,處理函數是application:
httpd = make_server('', 8000, application)
print "Serving HTTP on port 8000..."
# 開始監聽HTTP請求:
httpd.serve_forever()
確保以上兩個文件在同一個目錄下,而後在命令行輸入python server.py來啓動WSGI服務器:
注意:若是8000端口已被其餘程序佔用,啓動將失敗,請修改爲其餘端口。

啓動成功後,打開瀏覽器,輸入http://localhost:8000/,就能夠看到結果了:
按Ctrl+C終止服務器。

若是你以爲這個Web應用太簡單了,能夠稍微改造一下,從environ裏讀取PATH_INFO,這樣能夠顯示更加動態的內容:

# hello.py

def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return '<h1>Hello, %s!</h1>' % (environ['PATH_INFO'][1:] or 'web')
你能夠在地址欄輸入用戶名做爲URL的一部分,將返回Hello, xxx!:
不管多麼複雜的Web應用程序,入口都是一個WSGI處理函數。HTTP請求的全部輸入信息均可以經過environ得到,HTTP響應的輸出均可以經過start_response()加上函數返回值做爲Body。

複雜的Web應用程序,光靠一個WSGI函數來處理仍是太底層了,咱們須要在WSGI之上再抽象出Web框架,進一步簡化Web開發
過程分析

二:MVC和MTV模式程序員

2.1 MVCweb

MVC全名是Model View Controller,是模型(model)-視圖(view)-控制器(controller)的縮寫,一種軟件設計典範,用一種業務邏輯、數據、界面顯示分離的方法組織代碼,將業務邏輯彙集到一個部件裏面,在改進和個性化定製界面及用戶交互的同時,不須要從新編寫業務邏輯。MVC被獨特的發展起來用於映射傳統的輸入、處理和輸出功能在一個邏輯的圖形化用戶界面的結構中。

MVC開始是存在於桌面程序中的,M是指業務模型,V是指用戶界面,C則是控制器,使用MVC的目的是將M和V的實現代碼分離,從而使同一個程序可使用不一樣的表現形式。好比一批統計數據能夠分別用柱狀圖、餅圖來表示。C存在的目的則是確保M和V的同步,一旦M改變,V應該同步更新。[

Model(模型)表示應用程序核心(好比數據庫記錄列表)。
View(視圖)顯示數據(數據庫記錄)。
Controller(控制器)處理輸入(寫入數據庫記錄)。
MVC 模式同時提供了對 HTML、CSS 和 JavaScript 的徹底控制。
Model(模型)是應用程序中用於處理應用程序數據邏輯的部分。
  一般模型對象負責在數據庫中存取數據。
View(視圖)是應用程序中處理數據顯示的部分。
  一般視圖是依據模型數據建立的。
Controller(控制器)是應用程序中處理用戶交互的部分。
  一般控制器負責從視圖讀取數據,控制用戶輸入,並向模型發送數據。
MVC的簡介

2.2 MTV正則表達式

Django的MTV模式本質上與MVC模式沒有什麼差異,也是各組件之間爲了保持鬆耦合關係,只是定義上有些許不一樣,Django的MTV分別表明:

       Model(模型):負責業務對象與數據庫的對象(ORM)

       Template(模版):負責如何把頁面展現給用戶

       View(視圖):負責業務邏輯,並在適當的時候調用Model和Template

       此外,Django還有一個url分發器,它的做用是將一個個URL的頁面請求分發給不一樣的view處理,view再調用相應的Model和Template

Django是MTV框架。
MTV簡介

三:django的簡介(流程之命令行工具)shell

3.1  django的安裝數據庫

 

django  其實在這裏有2種安裝方式,一種是直接在命令提示符中安裝還有是在pycharm中直接去安裝, 
   命令提示符安裝:    pip3 install django ,這裏須要注意的一點是Python中好多東西都是經過pip3來進行實現的,因此要注意pip3的路徑是否是已經添加到環境變量中去,這個命令執行之後也要把django的路徑添加到環境變量中去,

1 建立project   :  在任意一個盤下打開命令提示符,  
django-admin startproject mysite,mysite是本身起的名字,可是由於這是國際慣例,因此就用mysite,下面是創建的mysite的結構:
---mysite
          ---settings.py
          ---url.py
          ---wsgi.py
---- manage.py    啓動文件
2.建立APP,python manage.py startapp app1
 
能夠在pycharm中直接去搜索django而後安裝就能夠,

3.2  django的命令行工具django

3.21   首先來建立一個django工程  django-admin.py startproject mysite,

django-admin.py 是Django的一個用於管理任務的命令行工具,manage.py是對django-admin.py的簡單包裝,每個Django Project裏都會有一個mannage.py。  生成的mysite工程目錄以下

  • manage.py ----- Django項目裏面的工具,經過它能夠調用django shell和數據庫等。
  • settings.py ---- 包含了項目的默認設置,包括數據庫信息,調試標誌以及其餘一些工做的變量。
  • urls.py ----- 負責把URL模式映射到應用程序。  

 3.22  在mysite下建立blog應用,能夠在命令提示符建立也能夠直接在pycharm中建立

3.23  啓動django項目   ,python manage.py runserver 8000,8000是本機的端口號,默認的,也能夠本身設置,而後咱們只須要訪問網址127.0.0.1:8080/  就能夠看到咱們所設計的內容

3.24  生成同步數據庫的腳本   python manage.py makemigrations

由於有時咱們所操做的數據咱們但願保存下來,其實全部的數據都在數據庫中存在,

同步數據庫:  python manage.py migrate

當咱們再去訪問一個頁面的時候咱們所操做的數據就會保存到數據庫中,

3.25 建立超級管理用戶  python manage.py createsuperuser  

3.26  清空數據庫   python manage.py flush

3.27  查詢某個命令的詳細信息  django-admin help startapp

這裏的admin是django自帶的一個後臺數據管理系統

3.28 啓動交互頁面  python manage.py shell

3.29 在pycharm終端上輸入python manage.py  就能夠查看詳細的列表

先來個簡單的顯示當前時間的例子來看一下具體的流程是什麼?

cur_time.html文件:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>當前時間{{ abc }}
</h1>
</body>
</html>

url.py:
 url(r"^cur_time",views.cur_time),

views.py:
def cur_time(request):
    times=datetime.datetime.now()
    #return HttpResponse("<h1>ok<h1>")

    return render(request,"cur_time.html",{"abc":times})
顯示時間

 在這裏要注意的是好比你在url.py寫路徑的收要跳轉到視圖函數中去,那麼久須要在此把模塊導入進去,可是有一點比較好的是,在咱們得這個settings裏面,django已經把環境變量給咱們配置好了,咱們就能夠直接的去引用模塊,

下面是個例子。來讓咱們看看究竟是怎樣運行的,以提交數據爲例

index.html文件顯示:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/userInfo/" method="post">
    <p>姓名<input type="text" name="username"></p>
    <p>性別<input type="text" name="sex"></p>
    <p>郵箱<input type="text" name="email"></p>
    <p><input type="submit" value="submit"></p>

</form>
{#<hr> 標籤在 HTML 頁面中建立一條水平線。#}
<hr>
<h1>數據展現</h1>
<table border="1px">
    <tr>
        <td>姓名</td>
        <td>性別</td>
        <td>郵箱</td>
    </tr>

    {% for i in user_list %}
    <tr>
        <td>{{ i.username }}</td>
        <td>{{ i.sex }}</td>
        <td>{{ i.email }}</td>
    </tr>
    {% endfor %}
</table>
</body>
</html>

{#<tr> 標籤訂義 HTML 表格中的行。一個tr標籤就是表明一行#}
{#tr 元素包含一個或多個 th 或 td 元素。這裏的td表示三列#}


url.py:
url(r"^userInfo",views.userInfo),

views.py:

#正常的提交數據並顯示
# user_list=[]
# def userInfo(req):
#     # req.POST:{"username":alex,}
#     if req.method=="POST":
#         username=req.POST.get("username",None)
#         sex=req.POST.get("sex",None)
#         email=req.POST.get("email",None)
#         user={"username":username,"sex":sex,"email":email}
#         user_list.append(user)
#     return render(req,"index.html",{"user_list":user_list})
提交數據顯示
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/userInfo/" method="post">
    <p>姓名<input type="text" name="username"></p>
    <p>性別<input type="text" name="sex"></p>
    <p>郵箱<input type="text" name="email"></p>
    <p><input type="submit" value="submit"></p>

</form>
{#<hr> 標籤在 HTML 頁面中建立一條水平線。#}
<hr>
<h1>數據展現</h1>
<table border="1px">
    <tr>
        <td>姓名</td>
        <td>性別</td>
        <td>郵箱</td>
    </tr>

    {% for i in user_list %}
    <tr>
        <td>{{ i.username }}</td>
        <td>{{ i.sex }}</td>
        <td>{{ i.email }}</td>
    </tr>
    {% endfor %}
</table>
</body>
</html>

{#<tr> 標籤訂義 HTML 表格中的行。一個tr標籤就是表明一行#}
{#tr 元素包含一個或多個 th 或 td 元素。這裏的td表示三列#}

url.py:
url(r"^userInfo",views.userInfo),

views.py:
#提交數據並在數據庫中顯示
def userInfo(req):
    if req.method=="POST":
        u=req.POST.get("username",None)
        s=req.POST.get("sex",None)
        e=req.POST.get("email",None)

        # ---------表中插入數據方式一
        # info={"username":u,"sex":e,"email":e}
        # models.UserInfor.objects.create(**info)

        # ---------表中插入數據方式二

        models.UserInfo.objects.create(
            username=u,
            sex=s,
            email=e,
        )

    user_list=models.UserInfo.objects.all()
    return render(req,"index.html",{"user_list":user_list})
提交數據(數據庫)

 四:django  URL(路由系統)

URL配置(URLconf)就像Django 所支撐網站的目錄。它的本質是URL模式以及要爲該URL模式調用的視圖函數之間的映射表;你就是以這種方式告訴Django,對於這個URL調用這段代碼,對於那個URL調用那段代碼。

urlpatterns = [
    url(正則表達式, views視圖函數,參數,別名),   
]
參數說明:

一個正則表達式字符串
一個可調用對象,一般爲一個視圖函數或一個指定視圖函數路徑的字符串
可選的要傳遞給視圖函數的默認參數(字典形式)
一個可選的name參數

來看一個url中的樣式代碼  nonamed group

"""mysite URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/1.10/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.conf.urls import url, include
    2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url
from django.contrib import admin
from blog import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r"^cur_time",views.cur_time),
    url(r"^userInfo",views.userInfo),

    #url(r"^articles/2003/$",views.special_case_2003),
    url(r'^articles/[0-9]{4}/$', views.year_archive),

    #url(r"^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$",views.year_archive),
    #參數三
    # url(r"^index/(?P<name>[0-9]){4}/",views.index,{"name":"alex"})
    #參數四
    url(r"^pay/index",views.index,name="alex"),
   # url(r"^blog",include("blog.urls")),
    url(r"login",views.login),
    url(r"home",views.home),
    url(r"add",views.add),

]
example
from django.conf.urls import url
from django.contrib import admin

from app01 import views

urlpatterns = [
   這裏面的^以及$和正則表達式裏面是同樣的就是以^後面的開頭以$前面的結尾
url(r'^articles/2003/$', views.special_case_2003),
    這裏是0到9的任意一個數字重複4次,就是至關於上面的2003,不過這裏有多樣性沒有定死了。可是好比說有2個url,若是在第一個以及顯示了那麼第二個就會被覆蓋就是它是從上往下開始匹配的,匹配到就結束。
#url(r'^articles/[0-9]{4}/$', views.year_archive),
這裏表示就是年月,可是看下面多加了一個括號,再去運行就會報錯顯示參數的個數不對,因此咱們就須要在視圖函數裏面得括號中再加一個參數就行
    url(r'^articles/([0-9]{4})/$', views.year_archive),  #no_named group

    url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),

    url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', 
這裏的加號就是重複數字1到無窮次,不管有多少都能匹配上
views.article_detail),

]
這些事url裏面的無名參數
代碼加註釋

named group

上面介紹的是一個無名分組,就會徹底按位子去匹配,這些參數都沒有名字,下面介紹一下有名分組即named group

那麼我想確定在說到正則這一塊確定好多人已經把這一部分的知識忘記的差很少了,因此呢,在講有名參數以前,咱們先來看一個小例子來回顧下當初的有些知識點

import re

ret=re.search('(?P<id>\d{3})/(?P<name>\w{3})','weeew34ttt123/ooo')
這裏的?P是固定的格式,表示這種分組是有名字的,匹配出一個id爲三個數字,一個name爲三個字母
print(ret.group())
print(ret.group('id'))
print(ret.group('name'))

 

這和上面的差很少,就是把無名改成有名參數
from django.conf.urls import url
  
from . import views
  
urlpatterns = [
    url(r'^articles/2003/$', views.special_case_2003),
    url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
    url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
    url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail),
]
有名參數例子
#參數三
# url(r"^index/(?P<name>[0-9]){4}/",views.index,{"name":"alex"})
#參數四
url(r"^pay/index",views.index,name="alex"), 這裏的name= 是固定格式
下面是一個登陸並提交數據的例子來看一下
login.html:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{#action   form 表單提交的路徑#}
{#<form action="/index/" method="post">#}
{#<form action={% url "alex" %} method="post">#}
{#    <input type="text" name="username">#}
{#    <input type="password" name="pwd">#}
{#    <input type="submit" value="submit">#}
{#</form>#}


<form action="/login/" method="post">
    <input type="text" name="username">
    <input type="password" name="pwd">
    <input type="submit" value="submit">
</form>



</body>
</html>

url.py:
url(r"^pay/index",views.index,name="alex"),

views.index

def index(req):
    if req.method == "POST":
        username = req.POST.get("username")
        pwd=req.POST.get("pwd")
        if username=="alex"and pwd=="123":
            return HttpResponse("登陸成功")
    return render(req,"login.html")
登陸函數

可是呢,咱們在url中寫的一些url中去匹配的路徑,有可能在上面匹配到之後下面的就覆蓋了,這是一個弊端,

因此呢,咱們能夠單建一個url,咱們不可能只有這一個blog APP,還會有其餘的,咱們能夠把每個應用的映射關係創建在本身的url.py中,這樣就不會亂了,也不會出現覆蓋的現象。

"""mysite URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/1.10/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.conf.urls import url, include
    2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url, include
from django.contrib import admin
from blog import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^blog/', include('blog.urls'))

]
建立本身的url

因此呢之後關於每個APP的,咱們均可以寫到它本身下的url中去

五:django views (視圖函數)

http請求中產生兩個核心對象:

http請求:HttpRequest對象

http響應:HttpResponse對象

所在位置:django.http

以前咱們用到的參數request就是HttpRequest    檢測方法:isinstance(request,HttpRequest)

5.1  HttpRequest 對象的屬性和方法

# path:       請求頁面的全路徑,不包括域名
#
# method:     請求中使用的HTTP方法的字符串表示。全大寫表示。例如
#
#                    if  req.method=="GET":
#
#                              do_something()
#
#                    elseif req.method=="POST":
#
#                              do_something_else()
#
# GET:         包含全部HTTP GET參數的類字典對象
#
# POST:       包含全部HTTP POST參數的類字典對象
#
#              服務器收到空的POST請求的狀況也是可能發生的,也就是說,表單form經過
#              HTTP POST方法提交請求,可是表單中可能沒有數據,所以不能使用
#              if req.POST來判斷是否使用了HTTP POST 方法;應該使用  if req.method=="POST"
#
#
#
# COOKIES:     包含全部cookies的標準Python字典對象;keys和values都是字符串。
#
# FILES:      包含全部上傳文件的類字典對象;FILES中的每個Key都是<input type="file" name="" />標籤中                     name屬性的值,FILES中的每個value同時也是一個標準的python字典對象,包含下面三個Keys:
#
#             filename:      上傳文件名,用字符串表示
#             content_type:   上傳文件的Content Type
#             content:       上傳文件的原始內容
#
#
# user:       是一個django.contrib.auth.models.User對象,表明當前登錄的用戶。若是訪問用戶當前
#              沒有登錄,user將被初始化爲django.contrib.auth.models.AnonymousUser的實例。你
#              能夠經過user的is_authenticated()方法來辨別用戶是否登錄:
#              if req.user.is_authenticated();只有激活Django中的AuthenticationMiddleware
#              時該屬性纔可用
#
# session:    惟一可讀寫的屬性,表明當前會話的字典對象;本身有激活Django中的session支持時該屬性纔可用。

#方法
get_full_path(),   好比:http://127.0.0.1:8000/index33/?name=123 ,req.get_full_path()獲得的結果就是/index33/?name=123
req.path:/index33
View Code

5.2 HttpResponse  對象

對於HttpRequest對象來講,是由django自動建立的,可是,HttpResponse對象就必須咱們本身建立。每一個view請求處理方法必須返回一個HttpResponse對象。

  HttpResponse類在django.http.HttpResponse

  在HttpResponse對象上擴展的經常使用方法:

頁面渲染:         render()(推薦)<br>                 render_to_response(),  這種後面你的括號裏面不用加參數
頁面跳轉:         redirect("路徑")
locals():    能夠直接將函數中全部的變量傳給模板
補充:

來看個例子

這是按順序分的,一個一個對應的
url.py:
    url(r"login",views.login),
    url(r"home",views.home),
    url(r"add",views.add),

views.py:
def login(req):
    if req.method=="POST":
        if 1:
           return redirect("/home/")
            # return render(req,"home.html",locals())
    return render(req,"login.html")
    # return redirect('login')
def home(req):
    name="小胡"
    return render(req,"home.html",{"name":name})

def add(req):
    # if req.method=="POST":
    #     return HttpResponse("OK")

    num=12
    return render(req,"add.html",locals())


login.html
home.html
add.html
View Code

六:Template 基礎

模板(Template)指C++程序設計語言中的函數模板與類別模板,大致對應於java和C#中的泛型。

你可能已經注意到咱們在例子視圖中返回文本的方式有點特別。 也就是說,HTML被直接硬編碼在 Python代碼之中。

def current_datetime(request):
    now = datetime.datetime.now()
    html = "<html><body>It is now %s.</body></html>" % now
    return HttpResponse(html)

儘管這種技術便於解釋視圖是如何工做的,但直接將HTML硬編碼到你的視圖裏卻並非一個好主意。 讓咱們來看一下爲何:

  • 對頁面設計進行的任何改變都必須對 Python 代碼進行相應的修改。 站點設計的修改每每比底層 Python 代碼的修改要頻繁得多,所以若是能夠在不進行 Python 代碼修改的狀況下變動設計,那將會方便得多。

  • Python 代碼編寫和 HTML 設計是兩項不一樣的工做,大多數專業的網站開發環境都將他們分配給不一樣的人員(甚至不一樣部門)來完成。 設計者和HTML/CSS的編碼人員不該該被要求去編輯Python的代碼來完成他們的工做。

  • 程序員編寫 Python代碼和設計人員製做模板兩項工做同時進行的效率是最高的,遠勝於讓一我的等待另外一我的完成對某個既包含 Python又包含 HTML 的文件的編輯工做。

基於這些緣由,將頁面的設計和Python的代碼分離開會更乾淨簡潔更容易維護。 咱們可使用 Django的 模板系統 (Template System)來實現這種模式,這就是本章要具體討論的問題。

6.1 模板的組成

 HTML代碼+邏輯控制代碼

6.2  邏輯控制代碼的組成 

6.21  變量    使用大括號來引用變量    它的格式爲:{{ name}}  ,就是在2個大括號裏面寫上變量名

    Template(模板)和Context(上下文) 對象     

return render(req,"home.html"他就是模板,{"name":name})這個就是上下文,來看一段代碼
3.22 render 方法的使用

>>> python manange.py shell  (進入該django項目的環境)
>>> from django.template import Context, Template
>>> t = Template('My name is {{ name }}.')
>>> c = Context({'name': 'Stephane'})
>>> t.render(c)
'My name is Stephane.'
View Code

可是咱們要進行這些操做不能直接在命令行中去操做,咱們首先要盡到django的環境中去才能執行

>>> python manange.py shell  (進入該django項目的環境)
>>> from django.template import Context, Template
>>> t = Template('My name is {{ name }}.')
>>> c = Context({'name': 'Stephane'})
>>> t.render(c)
'My name is Stephane.'


# 同一模板,多個上下文,一旦有了模板對象,你就能夠經過它渲染多個context,不管什麼時候咱們均可以
# 像這樣使用同一模板源渲染多個context,只進行 一次模板建立而後屢次調用render()方法渲染會
# 更爲高效:
# Low
for name in ('John', 'Julie', 'Pat'):
    t = Template('Hello, {{ name }}')
    print t.render(Context({'name': name}))
    這種方法在實現的時候模板建立了三遍,不太好下面的就是隻建立了一次
# Good
t = Template('Hello, {{ name }}')
for name in ('John', 'Julie', 'Pat'):
    print t.render(Context({'name': name}))
View Code

Django 模板解 析很是快捷。 大部分的解析工做都是在後臺經過對簡短正則表達式一次性調用來完成。 這和基於 XML 的模板引擎造成鮮明對比,那些引擎承擔了 XML 解析器的開銷,且每每比 Django 模板渲染引擎要慢上幾個數量級。

from django.shortcuts import render,HttpResponse
from django.template.loader import get_template #記得導入
# Create your views here.


import datetime
from django.template import Template,Context

# def current_time(req):
    #原始的視圖函數
    # now=datetime.datetime.now()
    # html="<html><body>如今時刻:<h1>%s.</h1></body></html>" %now
    # return HttpResponse(html)



# def current_time(req):

      #django模板修改的視圖函數
#     now=datetime.datetime.now()
#     t=Template('<html><body>如今時刻是:<h1 style="color:red">{{current_date}}</h1></body></html>')
      #t=get_template('current_datetime.html')
#     c=Context({'current_date':now})
#     html=t.render(c)
#     return HttpResponse(html)

#另外一種寫法(推薦)

def current_time(req):

    now=datetime.datetime.now()

    return render(req, 'current_datetime.html', {'current_date':now})

推薦方式
當前時間的例子

 6.23   深度變量的查找(萬能的句點號)         

在到目前爲止的例子中,咱們經過 context  傳遞的簡單參數值主要是字符串,然而,模板系統可以很是簡潔地處理更加複雜的數據結構,例如list、dictionary和自定義的對象。

在 Django 模板中遍歷複雜數據結構的關鍵是句點字符 (.)。

 

#最好是用幾個例子來講明一下。
# 首先,句點可用於訪問列表索引,例如:

>>> from django.template import Template, Context
>>> t = Template('Item 2 is {{ items.2 }}.')
>>> c = Context({'items': ['apples', 'bananas', 'carrots']})
>>> t.render(c)
'Item 2 is carrots.'

#假設你要向模板傳遞一個 Python 字典。 要經過字典鍵訪問該字典的值,可以使用一個句點:
>>> from django.template import Template, Context
>>> person = {'name': 'Sally', 'age': '43'}
>>> t = Template('{{ person.name }} is {{ person.age }} years old.')
>>> c = Context({'person': person})
>>> t.render(c)
'Sally is 43 years old.'

#一樣,也能夠經過句點來訪問對象的屬性。 比方說, Python 的 datetime.date 對象有
#year 、 month 和 day 幾個屬性,你一樣能夠在模板中使用句點來訪問這些屬性:

>>> from django.template import Template, Context
>>> import datetime
>>> d = datetime.date(1993, 5, 2)
>>> d.year
>>> d.month
>>> d.day
>>> t = Template('The month is {{ date.month }} and the year is {{ date.year }}.')
>>> c = Context({'date': d})
>>> t.render(c)
'The month is 5 and the year is 1993.'

# 這個例子使用了一個自定義的類,演示了經過實例變量加一點(dots)來訪問它的屬性,這個方法適
# 用於任意的對象。
>>> from django.template import Template, Context
>>> class Person(object):
...     def __init__(self, first_name, last_name):
...         self.first_name, self.last_name = first_name, last_name
>>> t = Template('Hello, {{ person.first_name }} {{ person.last_name }}.')
>>> c = Context({'person': Person('John', 'Smith')})
>>> t.render(c)
'Hello, John Smith.'

# 點語法也能夠用來引用對象的方法。 例如,每一個 Python 字符串都有 upper() 和 isdigit()
# 方法,你在模板中可使用一樣的句點語法來調用它們:
>>> from django.template import Template, Context
>>> t = Template('{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}')
>>> t.render(Context({'var': 'hello'}))
'hello -- HELLO -- False'
>>> t.render(Context({'var': '123'}))
'123 -- 123 -- True'

# 注意這裏調用方法時並* 沒有* 使用圓括號 並且也沒法給該方法傳遞參數;你只能調用不需參數的
# 方法。
萬能的句點號例子

 6.24  標籤(tag)的使用,結構爲:使用大括號和百分號的組合來使用tag{% %},裏面放的就是標籤,語句

6.241  {% if %}的使用

       {% if %}標籤計算一個變量值,若是是「true」,即它存在、不爲空而且不是false的boolean值,系統則會顯示{% if %}和{% endif %}間的全部內容

       須要注意的一點是隻要有一個if 開頭就必須有一個endif 來結束

{% if num >= 100 and 8 %}

    {% if num > 200 %}
        <p>num大於200</p>
    {% else %}
        <p>num大於100小於200</p>
    {% endif %}

{% elif num < 100%}
    <p>num小於100</p>

{% else %}
    <p>num等於100</p>

{% endif %}



{% if %} 標籤接受and,or或者not來測試多個變量值或者否認一個給定的變量
{% if %} 標籤不容許同一標籤裏同時出現and和or,不然邏輯容易產生歧義,例以下面的標籤是不合法的:

{% if obj1 and obj2 or obj3 %}
if例子代碼

 

6.242          {% for %}循環的使用

其實我感受和上面的if差很少,都是用來判斷條件的,須要注意的一點也是在最後面也須要加上結束的語句{%endfor%}

{% for %}標籤容許你按順序遍歷一個序列中的各個元素,每次循環模板系統都會渲染{% for %}和{% endfor %}之間的全部內容

<ul>
{% for obj in list %}
    <li>{{ obj.name }}</li>
{% endfor %}
</ul>


#在標籤裏添加reversed來反序循環列表:

    {% for obj in list reversed %}
    ...
    {% endfor %}

#{% for %}標籤能夠嵌套:

    {% for country in countries %}
        <h1>{{ country.name }}</h1>
        <ul>
         {% for city in country.city_list %}
            <li>{{ city }}</li>
         {% endfor %}
        </ul>
    {% endfor %}


#系統不支持中斷循環,系統也不支持continue語句,{% for %}標籤內置了一個forloop模板變量,
#這個變量含有一些屬性能夠提供給你一些關於循環的信息

1,forloop.counter表示循環的次數,它從1開始計數,第一次循環設爲1:

    {% for item in todo_list %}
        <p>{{ forloop.counter }}: {{ item }}</p>
    {% endfor %}
2,forloop.counter0 相似於forloop.counter,但它是從0開始計數,第一次循環設爲0
3,forloop.revcounter
4,forloop.revcounter0
5,forloop.first當第一次循環時值爲True,在特別狀況下頗有用:

    
    {% for object in objects %}   
         {% if forloop.first %}<li class="first">{% else %}<li>{% endif %}   
         {{ object }}   
        </li>  
    {% endfor %}  
    
# 富有魔力的forloop變量只能在循環中獲得,當模板解析器到達{% endfor %}時forloop就消失了
# 若是你的模板context已經包含一個叫forloop的變量,Django會用{% for %}標籤替代它
# Django會在for標籤的塊中覆蓋你定義的forloop變量的值
# 在其餘非循環的地方,你的forloop變量仍然可用


#{% empty %}

{{li }}
      {%  for i in li %}
          <li>{{ forloop.counter0 }}----{{ i }}</li>
      {% empty %}
          <li>this is empty!</li>
      {% endfor %}

#         [11, 22, 33, 44, 55]
#            0----11
#            1----22
#            2----33
#            3----44
#            4----55
for 循環例子

if for  均可以對列表,字典進行操做,和咱們得python中是同樣的
6.243   {%csrf_token%}:csrf_token標籤

用於生成csrf_token的標籤,用於防治跨站攻擊驗證。注意若是你在view的index裏用的是render_to_response方法,不會生效,若是想要實現須要這樣寫

from django.template import RequestContext,Template
return render_to_response("index.html",locals(),context_RequestContext(req))

 

     其實,這裏是會生成一個input標籤,和其餘表單標籤一塊兒提交給後臺的。

6.244         {% url %}:  引用路由配置的地址

<form action="{% url "bieming"%}" >
          <input type="text">
          <input type="submit"value="提交">
          {%csrf_token%}
</form>
View Code

6.245     {% with %}:用更簡單的變量名替代複雜的變量名

{% with total=fhjsaldfhjsdfhlasdfhljsdal %} {{ total }} {% endwith %}
View Code

6.246       {% verbatim %}: 禁止render

{% verbatim %}
         {{ hello }}
{% endverbatim %}
View Code

6.247  {% load %}:加載標籤庫 

 

6.3      自定義filter 和simple_tag

a.在app中建立template模塊(必須的,就得是這個名字)

b. 建立任意.py文件。如my_tags.py

 

from django import template
from django.utils.safestring import mark_safe
register = template.Library()  #register的是固定變量名,不能改變
#不能用於if語句,可多個參數
@register.simple_tag
def my_add100(v1):
    return v1 + 100

可是此時主要注意的是須要在HTML文件中開頭部分寫上{% load add100 %}這句話,就是把這個函數引用進來
#最多2個參數
# @register.filter
# def my_add100(v1,v2,v3):
#     return v1+100+v2+v3

@register.simple_tag
def my_input(id,arg):
    result = "<input type='text' id='%s' class='%s' />" %(id,arg,)
    return mark_safe(result)
View Code

 

------c、在使用自定義simple_tag和filter的html文件中導入以前建立的 my_tags.py :{% load my_tags %}

------d、使用simple_tag和filter(如何調用)

-------------------------------.html
{% load xxx %}   #首行
    
    
    
    
 # num=12
{{ num|filter_multi:2 }} #24

{{ num|filter_multi:"[22,333,4444]" }}


{% simple_tag_multi 2 5 %}  參數不限,但不能放在if for語句中
{% simple_tag_multi num 5 %}
View Code

e、在settings中的INSTALLED_APPS配置當前app,否則django沒法找到自定義的simple_tag.

注意:

filter能夠用在if等語句後,simple_tag不能夠

{% if num|filter_multi:30 > 100 %}
    {{ num|filter_multi:30 }}
{% endif %}

 

 

6.31   經常使用過濾器   語法格式{{obj|filter:param}}

 在操做一個對象的時候咱們也直接這樣:{{obj|upper}}所有大寫     {{obj|lower}}所有小寫   {{obj|first upper}}  第一個大寫可是隻取第一個      {{obj|capfirst}}  第一個大寫後面還會有

# 1  add          :   給變量加上相應的值
   #
   # 2  addslashes   :    給變量中的引號前加上斜線
   #
   # 3  capfirst     :    首字母大寫
   #
   # 4  cut          :   從字符串中移除指定的字符
   #
   # 5  date         :   格式化日期字符串
   #
   # 6  default      :   若是值是False,就替換成設置的默認值,不然就是用原本的值
   #
   # 7  default_if_none:  若是值是None,就替換成設置的默認值,不然就使用原本的值


#實例:

#value1="aBcDe"
{{ value1|upper }}<br>

#value2=5
{{ value2|add:3 }}<br>

#value3='he  llo wo r ld'
{{ value3|cut:' ' }}<br>

#import datetime
#value4=datetime.datetime.now()
{{ value4|date:'Y-m-d' }}<br>

#value5=[]
{{ value5|default:'空的' }}<br>

#value6='<a href="#">跳轉</a>'

{{ value6 }}

{% autoescape off %}
  {{ value6 }}
{% endautoescape %}

{{ value6|safe }}<br>

{{ value6|striptags }}

#value7='1234'
{{ value7|filesizeformat }}<br>
{{ value7|first }}<br>
{{ value7|length }}<br>
{{ value7|slice:":-1" }}<br>

#value8='http://www.baidu.com/?a=1&b=3'
{{ value8|urlencode }}<br>
    value9='hello I am yuan'
filter 的方法

 6.4                  extend  模板繼承

在講解了模板加載機制以後,咱們再介紹一個利用該機制的內建模板標籤: {% include %} 。該標籤容許在(模板中)包含其它的模板的內容。 標籤的參數是所要包含的模板名稱,能夠是一個變量,也能夠是用單/雙引號硬編碼的字符串。 每當在多個模板中出現相同的代碼時,就應該考慮是否要使用 {% include %} 來減小重複。

------extend(繼承)模板標籤

到目前爲止,咱們的模板範例都只是些零星的 HTML 片斷,但在實際應用中,你將用 Django 模板系統來建立整個 HTML 頁面。 這就帶來一個常見的 Web 開發問題: 在整個網站中,如何減小共用頁面區域(好比站點導航)所引發的重複和冗餘代碼?

解決該問題的傳統作法是使用 服務器端的 includes ,你能夠在 HTML 頁面中使用該指令將一個網頁嵌入到另外一箇中。 事實上, Django 經過剛纔講述的 {% include %} 支持了這種方法。 可是用 Django 解決此類問題的首選方法是使用更加優雅的策略—— 模板繼承 。

本質上來講,模板繼承就是先構造一個基礎框架模板,然後在其子模板中對它所包含站點公用部分和定義塊進行重載。

那麼來看一個例子,好比說訂單和購物車,其中好多代碼都是重複的,咱們把共同的代碼放在一塊兒,各自的放在一塊兒,進行調節使用

 

 from django.conf.urls import url
from django.contrib import admin
from blog import views
url(r"^orderd/",views.orderd),
    url(r"^shopping_car/",views.shopping_car),
URL
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .page-header{
            height: 50px;
            background-color: rebeccapurple;
        }

        .page-body .menu{
            height: 500px;
            background-color: antiquewhite;
            float: left;
            width: 20%;
        }

        .page-body .content{
            height: 500px;
            background-color: cornflowerblue;
            float: left;
            width: 80%;
        }
        .page-footer{
            height: 50px;
            background-color: darkcyan;
            clear: both;
        }
    </style>
</head>
<body>
<div>
    <div class="page-header"></div>
    <div class="page-body">
        <div class="menu">
            <a href="/orderd/">訂單</a><br>
            <a href="/shopping_car/">購物車</a>
        </div>




        <div class="content">

            {% block content %}
                <div class="xxx" style="color: red">pppppp</div>
            {% endblock %}

        </div>
    </div>
    <div class="page-footer"></div>


</div>
</body>
</html>
base.HTML公共部分
{% extends "base.html" %}


{% block content %}

       {{ block.super }}
       <div>訂單</div>

{% endblock %}
order.py
{% extends "base.html" %}


{% block content %}
        <div class="content">
            購物車2
        </div>
{% endblock %}
shopping_car

這個叫作 base.html 的模板定義了一個簡單的 HTML 框架文檔,咱們將在本站點的全部頁面中使用。 子模板的做用就是重載、添加或保留那些塊的內容。 (若是你一直按順序學習到這裏,保存這個文件到你的template目錄下,命名爲 base.html .)

咱們使用模板標籤: {% block %} 。 全部的 {% block %} 標籤告訴模板引擎,子模板能夠重載這些部分。 每一個{% block %}標籤所要作的是告訴模板引擎,該模板下的這一塊內容將有可能被子模板覆蓋。

如下是其工做方式。 在加載 current_datetime.html 模板時,模板引擎發現了 {% extends %} 標籤, 注意到該模板是一個子模板。 模板引擎當即裝載其父模板,即本例中的 base.html 。

此時,模板引擎注意到 base.html 中的三個 {% block %} 標籤,並用子模板的內容替換這些 block 。所以,引擎將會使用咱們在 block title %} 中定義的標題,對 {% block content %} 也是如此。 因此,網頁標題一塊將由{% block title %}替換,一樣地,網頁的內容一塊將由 {% block content %}替換。

注意因爲子模板並無定義 footer 塊,模板系統將使用在父模板中定義的值。 父模板 {% block %} 標籤中的內容老是被看成一條退路。

繼承並不會影響到模板的上下文。 換句話說,任何處在繼承樹上的模板均可以訪問到你傳到模板中的每個模板變量。

你能夠根據須要使用任意多的繼承次數。 使用繼承的一種常見方式是下面的三層法:

  1. 建立 base.html 模板,在其中定義站點的主要外觀感覺。 這些都是不常修改甚至從不修改的部分。

  1. 爲網站的每一個區域建立 base_SECTION.html 模板(例如, base_photos.html 和 base_forum.html )。這些模板對base.html 進行拓展,幷包含區域特定的風格與設計。

  1. 爲每種類型的頁面建立獨立的模板,例如論壇頁面或者圖片庫。 這些模板拓展相應的區域模板。

這個方法可最大限度地重用代碼,並使得向公共區域(如區域級的導航)添加內容成爲一件輕鬆的工做。

如下是使用模板繼承的一些訣竅:

  • 若是在模板中使用 {% extends %} ,必須保證其爲模板中的第一個模板標記。 不然,模板繼承將不起做用。

  • 通常來講,基礎模板中的 {% block %} 標籤越多越好。 記住,子模板沒必要定義父模板中全部的代碼塊,所以你能夠用合理的缺省值對一些代碼塊進行填充,而後只對子模板所需的代碼塊進行(重)定義。 俗話說,鉤子越多越好。

  • 若是發覺本身在多個模板之間拷貝代碼,你應該考慮將該代碼段放置到父模板的某個 {% block %} 中。

  • 若是你須要訪問父模板中的塊的內容,使用 {{ block.super }}這個標籤吧,這一個魔法變量將會表現出父模板中的內容。 若是隻想在上級代碼塊基礎上添加內容,而不是所有重載,該變量就顯得很是有用了。

  • 不容許在同一個模板中定義多個同名的 {% block %} 。 存在這樣的限制是由於block 標籤的工做方式是雙向的。 也就是說,block 標籤不只挖了一個要填的坑,也定義了在模板中這個坑所填充的內容。若是模板中出現了兩個相同名稱的 {% block %} 標籤,父模板將無從得知要使用哪一個塊的內容。

相關文章
相關標籤/搜索