http請求中產生兩個核心對象:
http請求:HttpRequest對象
http響應:HttpResponse對象
所在位置:django.http
之前我們用到的參數request就是HttpRequest 檢測方法:isinstance(request,HttpRequest)
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
注意一個常用方法:request.POST.getlist('')
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"yuan_back", views.yuan_back), -----------------------------------views.py def login(req): if req.method=="POST": if 1: # return redirect("/yuan_back/") name="yuanhao" return render(req,"my backend.html",locals()) return render(req,"login.html",locals()) def yuan_back(req): name="苑昊" return render(req,"my backend.html",locals()) -----------------------------------login.html <form action="/login/" 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> -----------------------------------my backend.html <h1>用戶{{ name }}你好</h1> #總結: render和redirect的區別: # 1 if render的頁面需要模板語言渲染,需要的將數據庫的數據加載到html,那么所有的這一部分 # 除了寫在yuan_back的視圖函數中,必須還要寫在login中,代碼重復,沒有解耦. # 2 the most important: url沒有跳轉到/yuan_back/,而是還在/login/,所以當刷新后 # 又得重新登錄.
七 Template基礎
模板系統的介紹
你可能已經注意到我們在例子視圖中返回文本的方式有點特別。 也就是說,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)來實現這種模式,這就是本章要具體討論的問題。
------------------------------------------------------------------模板語法------------------------------------------------------------------
一模版的組成
組成:HTML代碼+邏輯控制代碼
二 邏輯控制代碼的組成
1 變量(使用雙大括號來引用變量):
語法格式: {{var_name}}
------Template和Context對象
>>> 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}))
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}) 推薦方式
-----深度變量的查找(萬能的句點號)
在到目前為止的例子中,我們通過 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'
# 注意這里調用方法時並* 沒有* 使用圓括號 而且也無法給該方法傳遞參數;你只能調用不需參數的
# 方法。
-----變量的過濾器(filter)的使用
語法格式: {{obj|filter:param}}
# 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'
2 標簽(tag)的使用(使用大括號和百分比的組合來表示使用tag)
{% tags %}
------{% if %} 的使用
{% if %}標簽計算一個變量值,如果是“true”,即它存在、不為空並且不是false的boolean值,系統則會顯示{% 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 %}
------{% for %}的使用
{% 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
------{%csrf_token%}:csrf_token標簽
用於生成csrf_token的標簽,用於防治跨站攻擊驗證。注意如果你在view的index里用的是render_to_response方法,不會生效
其實,這里是會生成一個input標簽,和其他表單標簽一起提交給后台的。
------{% url %}: 引用路由配置的地址
<form action="{% url "bieming"%}" > <input type="text"> <input type="submit"value="提交"> {%csrf_token%} </form>
------{% with %}:用更簡單的變量名替代復雜的變量名
{% with total=fhjsaldfhjsdfhlasdfhljsdal %} {{ total }} {% endwith %}
------{% verbatim %}: 禁止render
{% verbatim %}
{{ hello }}
{% endverbatim %}
-----{% load %}: 加載標簽庫
3 自定義filter和simple_tag
------a、在app中創建templatetags模塊(必須的)
------b、創建任意 .py 文件,如:my_tags.py
from django import template from django.utils.safestring import mark_safe register = template.Library() #register的名字是固定的,不可改變 @register.filter def filter_multi(v1,v2): return v1 * v2 @register.simple_tag def simple_tag_multi(v1,v2): return v1 * v2 @register.simple_tag def my_input(id,arg): result = "<input type='text' id='%s' class='%s' />" %(id,arg,) return mark_safe(result)
------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 %}
-----e、在settings中的INSTALLED_APPS配置當前app,不然django無法找到自定義的simple_tag.
注意:
filter可以用在if等語句后,simple_tag不可以
{% if num|filter_multi:30 > 100 %}
{{ num|filter_multi:30 }}
{% endif %}
4 extend模板繼承
------include 模板標簽
在講解了模板加載機制之后,我們再介紹一個利用該機制的內建模板標簽: {% include %} 。該標簽允許在(模板中)包含其它的模板的內容。 標簽的參數是所要包含的模板名稱,可以是一個變量,也可以是用單/雙引號硬編碼的字符串。 每當在多個模板中出現相同的代碼時,就應該考慮是否要使用 {% include %} 來減少重復。
------extend(繼承)模板標簽
到目前為止,我們的模板范例都只是些零星的 HTML 片段,但在實際應用中,你將用 Django 模板系統來創建整個 HTML 頁面。 這就帶來一個常見的 Web 開發問題: 在整個網站中,如何減少共用頁面區域(比如站點導航)所引起的重復和冗余代碼?
解決該問題的傳統做法是使用 服務器端的 includes ,你可以在 HTML 頁面中使用該指令將一個網頁嵌入到另一個中。 事實上, Django 通過剛才講述的 {% include %} 支持了這種方法。 但是用 Django 解決此類問題的首選方法是使用更加優雅的策略—— 模板繼承 。
本質上來說,模板繼承就是先構造一個基礎框架模板,而后在其子模板中對它所包含站點公用部分和定義塊進行重載。
讓我們通過修改 current_datetime.html 文件,為 current_datetime 創建一個更加完整的模板來體會一下這種做法:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head> <title>The current time</title> </head> <body> <h1>My helpful timestamp site</h1> <p>It is now {{ current_date }}.</p> <hr> <p>Thanks for visiting my site.</p> </body> </html>
這看起來很棒,但如果我們要為 hours_ahead 視圖創建另一個模板會發生什么事情呢?
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head> <title>Future time</title> </head> <body> <h1>My helpful timestamp site</h1> <p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p> <hr> <p>Thanks for visiting my site.</p> </body> </html>
很明顯,我們剛才重復了大量的 HTML 代碼。 想象一下,如果有一個更典型的網站,它有導航條、樣式表,可能還有一些 JavaScript 代碼,事情必將以向每個模板填充各種冗余的 HTML 而告終。
解決這個問題的服務器端 include 方案是找出兩個模板中的共同部分,將其保存為不同的模板片段,然后在每個模板中進行 include。 也許你會把模板頭部的一些代碼保存為 header.html 文件:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head>
你可能會把底部保存到文件 footer.html :
<hr> <p>Thanks for visiting my site.</p> </body> </html>
對基於 include 的策略,頭部和底部的包含很簡單。 麻煩的是中間部分。 在此范例中,每個頁面都有一個<h1>My helpful timestamp site</h1> 標題,但是這個標題不能放在 header.html 中,因為每個頁面的 <title> 是不同的。 如果我們將 <h1> 包含在頭部,我們就不得不包含 <title> ,但這樣又不允許在每個頁面對它進行定制。 何去何從呢?
Django 的模板繼承系統解決了這些問題。 你可以將其視為服務器端 include 的逆向思維版本。 你可以對那些不同 的代碼段進行定義,而不是 共同 代碼段。
第一步是定義 基礎模板,該框架之后將由子模板所繼承。 以下是我們目前所講述范例的基礎模板:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head> <title>{% block title %}{% endblock %}</title> </head> <body> <h1>My helpful timestamp site</h1> {% block content %}{% endblock %} {% block footer %} <hr> <p>Thanks for visiting my site.</p> {% endblock %} </body> </html>
這個叫做 base.html 的模板定義了一個簡單的 HTML 框架文檔,我們將在本站點的所有頁面中使用。 子模板的作用就是重載、添加或保留那些塊的內容。 (如果你一直按順序學習到這里,保存這個文件到你的template目錄下,命名為 base.html .)
我們使用模板標簽: {% block %} 。 所有的 {% block %} 標簽告訴模板引擎,子模板可以重載這些部分。 每個{% block %}標簽所要做的是告訴模板引擎,該模板下的這一塊內容將有可能被子模板覆蓋。
現在我們已經有了一個基本模板,我們可以修改 current_datetime.html 模板來 使用它:
{% extends "base.html" %} {% block title %}The current time{% endblock %} {% block content %} <p>It is now {{ current_date }}.</p> {% endblock %}
再為 hours_ahead 視圖創建一個模板,看起來是這樣的:
{% extends "base.html" %} {% block title %}Future time{% endblock %} {% block content %} <p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p> {% endblock %}
看起來很漂亮是不是? 每個模板只包含對自己而言 獨一無二 的代碼。 無需多余的部分。 如果想進行站點級的設計修改,僅需修改 base.html ,所有其它模板會立即反映出所作修改。
以下是其工作方式:
在加載 current_datetime.html 模板時,模板引擎發現了 {% extends %} 標簽, 注意到該模板是一個子模板。 模板引擎立即裝載其父模板,即本例中的 base.html 。此時,模板引擎注意到 base.html 中的三個 {% block %} 標簽,並用子模板的內容替換這些 block 。因此,引擎將會使用我們在 { block title %} 中定義的標題,對 {% block content %} 也是如此。 所以,網頁標題一塊將由{% block title %}替換,同樣地,網頁的內容一塊將由 {% block content %}替換。
注意由於子模板並沒有定義 footer 塊,模板系統將使用在父模板中定義的值。 父模板 {% block %} 標簽中的內容總是被當作一條退路。繼承並不會影響到模板的上下文。 換句話說,任何處在繼承樹上的模板都可以訪問到你傳到模板中的每一個模板變量。你可以根據需要使用任意多的繼承次數。 使用繼承的一種常見方式是下面的三層法:
<1> 創建 base.html 模板,在其中定義站點的主要外觀感受。 這些都是不常修改甚至從不修改的部分。 <2> 為網站的每個區域創建 base_SECTION.html 模板(例如, base_photos.html 和 base_forum.html )。這些模板對base.html 進行拓展, 並包含區域特定的風格與設計。 <3> 為每種類型的頁面創建獨立的模板,例如論壇頁面或者圖片庫。 這些模板拓展相應的區域模板。
這個方法可最大限度地重用代碼,並使得向公共區域(如區域級的導航)添加內容成為一件輕松的工作。
以下是使用模板繼承的一些訣竅:
<1>如果在模板中使用 {% extends %} ,必須保證其為模板中的第一個模板標記。 否則,模板繼承將不起作用。 <2>一般來說,基礎模板中的 {% block %} 標簽越多越好。 記住,子模板不必定義父模板中所有的代碼塊,因此 你可以用合理的缺省值對一些代碼塊進行填充,然后只對子模板所需的代碼塊進行(重)定義。 俗話說,鈎子越 多越好。 <3>如果發覺自己在多個模板之間拷貝代碼,你應該考慮將該代碼段放置到父模板的某個 {% block %} 中。 如果你需要訪問父模板中的塊的內容,使用 {{ block.super }}這個標簽吧,這一個魔法變量將會表現出父模 板中的內容。 如果只想在上級代碼塊基礎上添加內容,而不是全部重載,該變量就顯得非常有用了。 <4>不允許在同一個模板中定義多個同名的 {% block %} 。 存在這樣的限制是因為block 標簽的工作方式是雙向的。 也就是說,block 標簽不僅挖了一個要填的坑,也定義了在父模板中這個坑所填充的內容。如果模板中出現了兩個 相同名稱的 {% block %} 標簽,父模板將無從得知要使用哪個塊的內容。