Django基礎(一)
Django基礎(一)
一 什麽是web框架?
框架,即framework,特指為解決一個開放性問題而設計的具有一定約束性的支撐結構,使用框架可以幫你快速開發特定的系統,簡單地說,就是你用別人搭建好的舞臺來做表演。
對於所有的Web應用,本質上其實就是一個socket服務端,用戶的瀏覽器其實就是一個socket客戶端。
View Code最簡單的Web應用就是先把HTML用文件保存好,用一個現成的HTTP服務器軟件,接收用戶請求,從文件中讀取HTML,返回。
如果要動態生成HTML,就需要把上述步驟自己來實現。不過,接受HTTP請求、解析HTTP請求、發送HTTP響應都是苦力活,如果我們自己來寫這些底層代碼,還沒開始寫動態HTML呢,就得花個把月去讀HTTP規範。
正確的做法是底層代碼由專門的服務器軟件實現,我們用Python專註於生成HTML文檔。因為我們不希望接觸到TCP連接、HTTP原始請求和響應格式,所以,需要一個統一的接口,讓我們專心用Python編寫Web業務。
這個接口就是WSGI:Web Server Gateway Interface。
-----------------------------Do a web framework ourselves---------------------------
step 1:
View Code註意:
View Codestep 2
View Codestep3
View Codestep4
View Code夥計們,不知不覺我們自己已經寫出一個web框架啦!
二 MVC和MTV模式
Django的MTV模式本質是各組件之間為了保持松耦合關系,Django的MTV分別代表:
Model(模型):負責業務對象與數據庫的對象(ORM)
Template(模版):負責如何把頁面展示給用戶
View(視圖):負責業務邏輯,並在適當的時候調用Model和Template
此外,Django還有一個url分發器,它的作用是將一個個URL的頁面請求分發給不同的view處理,view再調用相應的Model和Template
Django基本命令
1、創建一個django project
django-admin.py startproject mysite
當前目錄下會生成mysite的工程,目錄結構如下:
- manage.py ----- Django項目裏面的工具,通過它可以調用django shell和數據庫等。
- settings.py ---- 包含了項目的默認設置,包括數據庫信息,調試標誌以及其他一些工作的變量。
- urls.py ----- 負責把URL模式映射到應用程序。
2、在mysite目錄下創建應用,比如blog:
python manage.py startapp blog
3、啟動django項目
python manage.py runserver 8080
這樣我們的django就啟動起來了!當我們訪問:http://127.0.0.1:8080/時就可以看到:
4、同步更改數據庫表或字段
‘‘‘ python manage.py syncdb 註意:Django 1.7.1 及以上的版本需要用以下命令 python manage.py makemigrations python manage.py migrate ‘‘‘
這種方法可以創建表,當你在models.py中新增了類時,運行它就可以自動在數據庫中創建表了,不用手動創建。
5、清空數據庫
python manage.py flush
此命令會詢問是 yes 還是 no, 選擇 yes 會把數據全部清空掉,只留下空表。
6、創建超級管理員
‘‘‘ python manage.py createsuperuser # 按照提示輸入用戶名和對應的密碼就好了郵箱可以留空,用戶名和密碼必填 # 修改 用戶密碼可以用: python manage.py changepassword username ‘‘‘
7、Django 項目環境終端
python manage.py shell
這個命令和 直接運行 python 進入 shell 的區別是:你可以在這個 shell 裏面調用當前項目的 models.py 中的 API,對於操作數據的測試非常方便。
8、Django 項目環境終端
ython manage.py dbshell
Django 會自動進入在settings.py中設置的數據庫,如果是 MySQL 或 postgreSQL,會要求輸入數據庫用戶密碼。
在這個終端可以執行數據庫的SQL語句。如果您對SQL比較熟悉,可能喜歡這種方式。
9、更多命令
python manage.py
查看所有的命令,忘記子名稱的時候特別有用。
10 static配置
View Code二 路由配置系統(URLconf)
URL配置(URLconf)就像Django 所支撐網站的目錄。它的本質是URL與要為該URL調用的視圖函數之間的映射表;你就是以這種方式告訴Django,對於這個URL調用這段代碼,對於那個URL調用那段代碼。
‘‘‘
urlpatterns = [
url(正則表達式, views視圖函數,參數,別名),
]
參數說明:
一個正則表達式字符串
一個可調用對象,通常為一個視圖函數或一個指定視圖函數路徑的字符串
可選的要傳遞給視圖函數的默認參數(字典形式)
一個可選的name參數
‘‘‘
2.1 URLconf的正則字符串參數
2.1.1 簡單配置
from django.conf.urls import url
from . import views
urlpatterns = [
url(r‘^articles/2003/$‘, views.special_case_2003),
url(r‘^articles/([0-9]{4})/$‘, views.year_archive),
url(r‘^articles/([0-9]{4})/([0-9]{2})/$‘, views.month_archive),
url(r‘^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$‘, views.article_detail),
]
‘‘‘ NOTE: 1 一旦匹配成功則不再繼續 2 若要從URL 中捕獲一個值,只需要在它周圍放置一對圓括號。 3 不需要添加一個前導的反斜杠,因為每個URL 都有。例如,應該是^articles 而不是 ^/articles。 4 每個正則表達式前面的‘r‘ 是可選的但是建議加上。 一些請求的例子: /articles/2005/3/ 不匹配任何URL 模式,因為列表中的第三個模式要求月份應該是兩個數字。 /articles/2003/ 將匹配列表中的第一個模式不是第二個,因為模式按順序匹配,第一個會首先測試是否匹配。 /articles/2005/03/ 請求將匹配列表中的第三個模式。Django 將調用函數 views.month_archive(request, ‘2005‘, ‘03‘)。 ‘‘‘SLASH
2.1.2 有名分組(named group)
上面的示例使用簡單的、沒有命名的正則表達式組(通過圓括號)來捕獲URL 中的值並以位置 參數傳遞給視圖。在更高級的用法中,可以使用命名的正則表達式組來捕獲URL 中的值並以關鍵字 參數傳遞給視圖。
在Python 正則表達式中,命名正則表達式組的語法是(?P<name>pattern)
,其中name
是組的名稱,pattern
是要匹配的模式。
下面是以上URLconf 使用命名組的重寫:
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),
]
這個實現與前面的示例完全相同,只有一個細微的差別:捕獲的值作為關鍵字參數而不是位置參數傳遞給視圖函數。例如:
/articles/2005/03/
請求將調用views.month_archive(request, year=‘2005‘, month=‘03‘)函數
/articles/2003/03/03/
請求將調用函數views.article_detail(request, year=‘2003‘, month=‘03‘, day=‘03‘)。
在實際應用中,這意味你的URLconf 會更加明晰且不容易產生參數順序問題的錯誤 —— 你可以在你的視圖函數定義中重新安排參數的順序。當然,這些好處是以簡潔為代價;有些開發人員認為命名組語法醜陋而繁瑣。
2.1.3 URLconf 在什麽上查找
URLconf 在請求的URL 上查找,將它當做一個普通的Python 字符串。不包括GET和POST參數以及域名。
例如,http://www.example.com/myapp/ 請求中,URLconf 將查找myapp/
。
在http://www.example.com/myapp/?page=3 請求中,URLconf 仍將查找myapp/
。
URLconf 不檢查請求的方法。換句話講,所有的請求方法 —— 同一個URL的POST
、GET
、HEAD
等等 —— 都將路由到相同的函數。
2.1.4 捕獲的參數永遠是字符串
每個捕獲的參數都作為一個普通的Python 字符串傳遞給視圖,無論正則表達式使用的是什麽匹配方式。例如,下面這行URLconf 中:
url(r‘^articles/(?P<year>[0-9]{4})/$‘, views.year_archive),
views.year_archive()
的year
參數將是一個字符串
2.1.5 指定視圖參數的默認值
有一個方便的小技巧是指定視圖參數的默認值。 下面是一個URLconf 和視圖的示例:
# URLconf
from django.conf.urls import url
from . import views
urlpatterns = [
url(r‘^blog/$‘, views.page),
url(r‘^blog/page(?P<num>[0-9]+)/$‘, views.page),
]
# View (in blog/views.py)
def page(request, num="1"):
...
在上面的例子中,兩個URL模式指向同一個視圖views.page
—— 但是第一個模式不會從URL 中捕獲任何值。如果第一個模式匹配,page()
函數將使用num
參數的默認值"1"。如果第二個模式匹配,page()
將使用正則表達式捕獲的num
值。
2.1.6 Including other URLconfs
#At any point, your urlpatterns can “include” other URLconf modules. This
#essentially “roots” a set of URLs below other ones.
#For example, here’s an excerpt of the URLconf for the Django website itself.
#It includes a number of other URLconfs:
from django.conf.urls import include, url
urlpatterns = [
url(r‘^admin/‘, admin.site.urls),
url(r‘^blog/‘, include(‘blog.urls‘)),
]
2.2 傳遞額外的選項給視圖函數(了解)
URLconfs 具有一個鉤子,讓你傳遞一個Python 字典作為額外的參數傳遞給視圖函數。
django.conf.urls.url()
函數可以接收一個可選的第三個參數,它是一個字典,表示想要傳遞給視圖函數的額外關鍵字參數。
例如:
from django.conf.urls import url
from . import views
urlpatterns = [
url(r‘^blog/(?P<year>[0-9]{4})/$‘, views.year_archive, {‘foo‘: ‘bar‘}),
]
在這個例子中,對於/blog/2005/
請求,Django 將調用views.year_archive(request, year=‘2005‘, foo=‘bar‘)
。
這個技術在Syndication 框架中使用,來傳遞元數據和選項給視圖。
2.3 name參數
View Code三 編寫視圖
一個視圖函數,或者簡短來說叫做視圖,是一個簡單的Python函數,它接受web請求,並且返回web響應。響應可以是一張網頁的HTML內容,一個重定向,一個404錯誤,一個XML文檔,或者一張圖片. . . 是任何東西都可以。無論視圖本身包含什麽邏輯,都要返回響應。代碼寫在哪裏也無所謂,只要它在你的Python目錄下面。除此之外沒有更多的要求了——可以說“沒有什麽神奇的地方”。為了能夠把代碼放在某個地方,慣例是把視圖放在叫做views.py的文件中,然後把它放到你的項目或者應用目錄裏。
3.1 一個簡單的視圖
下面是一個返回當前日期和時間作為HTML文檔的視圖:
from django.http import HttpResponse
import datetime
def current_datetime(request):
now = datetime.datetime.now()
html = "<html><body>It is now %s.</body></html>" % now
return HttpResponse(html)
讓我們逐行閱讀上面的代碼:
- 首先,我們從 django.http模塊導入了HttpResponse類,以及Python的datetime庫。
- 接著,我們定義了current_datetime函數。它是一個視圖函數。每個視圖函數都應接收HttpRequest對象作為第一個參數,一般叫做request。
- 註意視圖函數的名稱並不重要;不需要用一個統一的命名方式來命名,以便讓Django識別它。我們將其命名為current_datetime,是因為這個名稱能夠精確地反映出它的功能。
- 這個視圖會返回一個HttpResponse對象,其中包含生成的響應。每個視圖函數都要返回HttpResponse對象
‘‘‘
http請求-響應過程中有兩個核心對象:
http請求對象:HttpRequest
http響應響應:HttpResponse
所在位置:django.http
‘‘‘
3.2 快捷函數
3.2.1 render函數
---------------render(request, template_name[, context])
結合一個給定的模板和一個給定的上下文字典,並返回一個渲染後的 HttpResponse 對象。
參數:
request: 用於生成響應的請求對象。
template_name:要使用的模板的完整名稱,可選的參數
context:添加到模板上下文的一個字典。默認是一個空字典。如果字典中的某個值是可調用的,視圖將在渲染模板之前調用它。
content_type:生成的文檔要使用的MIME類型。默認為DEFAULT_CONTENT_TYPE 設置的值。
status:響應的狀態碼。默認為200。
3.2.2 redirect函數
-----------------------------------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
4.1 模板系統的介紹
你可能已經註意到我們在例子視圖中返回文本的方式有點特別。 也就是說,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)來實現這種模式,這就是本章要具體討論的問題。
python的模板:HTML代碼+邏輯控制代碼
4.2 模板支持的語法
4.2.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 模板渲染引擎要慢上幾個數量級。
View Code4.2.2 深度變量的查找(萬能的句點號)
在到目前為止的例子中,我們通過 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
1993
>>> d.month
5
>>> d.day
2
>>> 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‘
# 註意這裏調用方法時並* 沒有* 使用圓括號 而且也無法給該方法傳遞參數;你只能調用不需參數的
# 方法。
4.2.3 變量的過濾器(filter)的使用
語法格式: {{obj|filter:param}}View Code
4.2.4 標簽(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的標簽,用於防治跨站攻擊驗證。 其實,這裏是會生成一個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 %}
略
加載標簽庫:自定義filter和simple_tag
a、在app中創建templatetags模塊(必須的)
b、創建任意 .py 文件,如:my_tags.py
View Codec、在使用自定義simple_tag和filter的html文件中導入之前創建的 my_tags.py :{% load my_tags %}
d、使用simple_tag和filter(如何調用)
View Codee、在settings中的INSTALLED_APPS配置當前app,不然django無法找到自定義的simple_tag.
註意:
filter可以用在if等語句後,simple_tag不可以
{% if num|filter_multi:30 > 100 %}
{{ num|filter_multi:30 }}
{% endif %}
extend模板繼承
到目前為止,我們的模板範例都只是些零星的 HTML 片段,但在實際應用中,你將用 Django 模板系統來創建整個 HTML 頁面。 這就帶來一個常見的 Web 開發問題: 在整個網站中,如何減少共用頁面區域(比如站點導航)所引起的重復和冗余代碼?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>
Django 的模板繼承系統解決了這些問題。 你可以將其視為服務器端 include 的逆向思維版本。 你可以對那些不同 的代碼段進行定義,而不是 共同 代碼段。
第一步是定義 基礎模板,該框架之後將由子模板所繼承。 以下是我們目前所講述範例的基礎模板:
View Code這個叫做 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 %} 標簽,父模板將無從得知要使用哪個塊的內容。
五 數據庫與ORM
5.1 數據庫的配置
1 django默認支持sqlite,mysql, oracle,postgresql數據庫。
<1> sqlite
django默認使用sqlite的數據庫,默認自帶sqlite的數據庫驅動 , 引擎名稱:django.db.backends.sqlite3
<2> mysql
引擎名稱:django.db.backends.mysql
2 mysql驅動程序
- MySQLdb(mysql python)
- mysqlclient
- MySQL
- PyMySQL(純python的mysql驅動程序)
3 在django的項目中會默認使用sqlite數據庫,在settings裏有如下設置:
如果我們想要更改數據庫,需要修改如下:
View Code
註意:
View Code5.2 ORM表模型
表(模型)的創建:
實例:我們來假定下面這些概念,字段和關系
作者模型:一個作者有姓名。
作者詳細模型:把作者的詳情放到詳情表,包含性別,email地址和出生日期,作者詳情模型和作者模型之間是一對一的關系(one-to-one)(類似於每個人和他的身份證之間的關系),在大多數情況下我們沒有必要將他們拆分成兩張表,這裏只是引出一對一的概念。
出版商模型:出版商有名稱,地址,所在城市,省,國家和網站。
書籍模型:書籍有書名和出版日期,一本書可能會有多個作者,一個作者也可以寫多本書,所以作者和書籍的關系就是多對多的關聯關系(many-to-many),一本書只應該由一個出版商出版,所以出版商和書籍是一對多關聯關系(one-to-many),也被稱作外鍵。
View Code分析代碼:
<1> 每個數據模型都是django.db.models.Model的子類,它的父類Model包含了所有必要的和數據庫交互的方法。並提供了一個簡介漂亮的定義數據庫字段的語法。
<2> 每個模型相當於單個數據庫表(多對多關系例外,會多生成一張關系表),每個屬性也是這個表中的字段。屬性名就是字段名,它的類型(例如CharField)相當於數據庫的字段類型(例如varchar)。大家可以留意下其它的類型都和數據庫裏的什麽字段對應。
<3> 模型之間的三種關系:一對一,一對多,多對多。
一對一:實質就是在主外鍵(author_id就是foreign key)的關系基礎上,給外鍵加了一個UNIQUE=True的屬性;
一對多:就是主外鍵關系;(foreign key)
多對多:(ManyToManyField) 自動創建第三張表(當然我們也可以自己創建第三張表:兩個foreign key)
ORM之增(create,save)
from app01.models import *
#create方式一: Author.objects.create(name=‘Alvin‘)
#create方式二: Author.objects.create(**{"name":"alex"})
#save方式一: author=Author(name="alvin")
author.save()
#save方式二: author=Author()
author.name="alvin"
author.save()
重點來了------->那麽如何創建存在一對多或多對多關系的一本書的信息呢?(如何處理外鍵關系的字段如一對多的publisher和
多對多的authors)
ORM之刪(delete)
>>> Book.objects.filter(id=1).delete()
(3, {‘app01.Book_authors‘: 2, ‘app01.Book‘: 1})
我們表面上刪除了一條信息,實際卻刪除了三條,因為我們刪除的這本書在Book_authors表中有兩條相關信息,這種刪除方式就是django默認的級聯刪除。
ORM之改(update和save)
實例:
註意:
<1> 第二種方式修改不能用get的原因是:update是QuerySet對象的方法,get返回的是一個model對象,它沒有update方法,而filter返回的是一個QuerySet對象(filter裏面的條件可能有多個條件符合,比如name=‘alvin‘,可能有兩個name=‘alvin‘的行數據)。
<2>在“插入和更新數據”小節中,我們有提到模型的save()方法,這個方法會更新一行裏的所有列。 而某些情況下,我們只需要更新行裏的某幾列。
View Code此外,update()方法對於任何結果集(QuerySet)均有效,這意味著你可以同時更新多條記錄update()方法會返回一個整型數值,表示受影響的記錄條數。
註意,這裏因為update返回的是一個整形,所以沒法用query屬性;對於每次創建一個對象,想顯示對應的raw sql,需要在settings加上日誌記錄部分:
View CodeORM之查(filter,value)
查詢API:
# 查詢相關API: # <1>filter(**kwargs): 它包含了與所給篩選條件相匹配的對象 # <2>all(): 查詢所有結果 # <3>get(**kwargs): 返回與所給篩選條件相匹配的對象,返回結果有且只有一個,如果符合篩選條件的對象超過一個或者沒有都會拋出錯誤。 #-----------下面的方法都是對查詢的結果再進行處理:比如 objects.filter.values()-------- # <4>values(*field): 返回一個ValueQuerySet——一個特殊的QuerySet,運行後得到的並不是一系列 model的實例化對象,而是一個可叠代的字典序列 # <5>exclude(**kwargs): 它包含了與所給篩選條件不匹配的對象 # <6>order_by(*field): 對查詢結果排序 # <7>reverse(): 對查詢結果反向排序 # <8>distinct(): 從返回結果中剔除重復紀錄 # <9>values_list(*field): 它與values()非常相似,它返回的是一個元組序列,values返回的是一個字典序列 # <10>count(): 返回數據庫中匹配查詢(QuerySet)的對象數量。 # <11>first(): 返回第一條記錄 # <12>last(): 返回最後一條記錄 # <13>exists(): 如果QuerySet包含數據,就返回True,否則返回FalseView Code
QuerySet與惰性機制
所謂惰性機制:Publisher.objects.all()或者.filter()等都只是返回了一個QuerySet(查詢結果集對象),它並不會馬上執行sql,而是當調用QuerySet的時候才執行。
QuerySet特點:
<1> 可叠代的
<2> 可切片
#objs=models.Book.objects.all()#[obj1,obj2,ob3...] #QuerySet: 可叠代 # for obj in objs:#每一obj就是一個行對象 # print("obj:",obj) # QuerySet: 可切片 # print(objs[1]) # print(objs[1:4]) # print(objs[::-1])
QuerySet的高效使用:
View Code對象查詢,單表條件查詢,多表條件關聯查詢
#--------------------對象形式的查找--------------------------
# 正向查找
ret1=models.Book.objects.first()
print(ret1.title)
print(ret1.price)
print(ret1.publisher)
print(ret1.publisher.name) #因為一對多的關系所以ret1.publisher是一個對象,而不是一個queryset集合
# 反向查找
ret2=models.Publish.objects.last()
print(ret2.name)
print(ret2.city)
#如何拿到與它綁定的Book對象呢?
print(ret2.book_set.all()) #ret2.book_set是一個queryset集合
#---------------了不起的雙下劃線(__)之單表條件查詢----------------
# models.Tb1.objects.filter(id__lt=10, id__gt=1) # 獲取id大於1 且 小於10的值
#
# models.Tb1.objects.filter(id__in=[11, 22, 33]) # 獲取id等於11、22、33的數據
# models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in
#
# models.Tb1.objects.filter(name__contains="ven")
# models.Tb1.objects.filter(name__icontains="ven") # icontains大小寫不敏感
#
# models.Tb1.objects.filter(id__range=[1, 2]) # 範圍bettwen and
#
# startswith,istartswith, endswith, iendswith,
#----------------了不起的雙下劃線(__)之多表條件關聯查詢---------------
# 正向查找(條件)
# ret3=models.Book.objects.filter(title=‘Python‘).values(‘id‘)
# print(ret3)#[{‘id‘: 1}]
#正向查找(條件)之一對多
ret4=models.Book.objects.filter(title=‘Python‘).values(‘publisher__city‘)
print(ret4) #[{‘publisher__city‘: ‘北京‘}]
#正向查找(條件)之多對多
ret5=models.Book.objects.filter(title=‘Python‘).values(‘author__name‘)
print(ret5)
ret6=models.Book.objects.filter(author__name="alex").values(‘title‘)
print(ret6)
#註意
#正向查找的publisher__city或者author__name中的publisher,author是book表中綁定的字段
#一對多和多對多在這裏用法沒區別
# 反向查找(條件)
#反向查找之一對多:
ret8=models.Publisher.objects.filter(book__title=‘Python‘).values(‘name‘)
print(ret8)#[{‘name‘: ‘人大出版社‘}] 註意,book__title中的book就是Publisher的關聯表名
ret9=models.Publisher.objects.filter(book__title=‘Python‘).values(‘book__authors‘)
print(ret9)#[{‘book__authors‘: 1}, {‘book__authors‘: 2}]
#反向查找之多對多:
ret10=models.Author.objects.filter(book__title=‘Python‘).values(‘name‘)
print(ret10)#[{‘name‘: ‘alex‘}, {‘name‘: ‘alvin‘}]
#註意
#正向查找的book__title中的book是表名Book
#一對多和多對多在這裏用法沒區別
註意:條件查詢即與對象查詢對應,是指在filter,values等方法中的通過__來明確查詢條件。
聚合查詢和分組查詢
<1> aggregate(*args,**kwargs):
通過對QuerySet進行計算,返回一個聚合值的字典。aggregate()中每一個參數都指定一個包含在字典中的返回值。即在查詢集上生成聚合。
View Code<2> annotate(*args,**kwargs):
可以通過計算查詢結果中每一個對象所關聯的對象集合,從而得出總計值(也可以是平均值或總和),即為查詢集的每一項生成聚合。
查詢alex出的書總價格
查詢各個作者出的書的總價格,這裏就涉及到分組了,分組條件是authors__name
查詢各個出版社最便宜的書價是多少
F查詢和Q查詢
僅僅靠單一的關鍵字參數查詢已經很難滿足查詢要求。此時Django為我們提供了F和Q查詢:
View Codeadmin的配置
admin是django強大功能之一,它能共從數據庫中讀取數據,呈現在頁面中,進行管理。默認情況下,它的功能已經非常強大,如果你不需要復雜的功能,它已經夠用,但是有時候,一些特殊的功能還需要定制,比如搜索功能,下面這一系列文章就逐步深入介紹如何定制適合自己的admin應用。
如果你覺得英文界面不好用,可以在setting.py 文件中修改以下選項
LANGUAGE_CODE = ‘en-us‘ #LANGUAGE_CODE = ‘zh-hans‘
一 認識ModelAdmin
管理界面的定制類,如需擴展特定的model界面需從該類繼承。
二 註冊medel類到admin的兩種方式:
<1> 使用register的方法
admin.site.register(Book,MyAdmin)
<2> 使用register的裝飾器
@admin.register(Book)
三 掌握一些常用的設置技巧
- list_display: 指定要顯示的字段
- search_fields: 指定搜索的字段
- list_filter: 指定列表過濾器
- ordering: 指定排序字段
from django.contrib import admin
from app01.models import *
# Register your models here.
# @admin.register(Book)#----->單給某個表加一個定制
class MyAdmin(admin.ModelAdmin):
list_display = ("title","price","publisher")
search_fields = ("title","publisher")
list_filter = ("publisher",)
ordering = ("price",)
fieldsets =[
(None, {‘fields‘: [‘title‘]}),
(‘price information‘, {‘fields‘: [‘price‘,"publisher"], ‘classes‘: [‘collapse‘]}),
]
admin.site.register(Book,MyAdmin)
admin.site.register(Publish)
admin.site.register(Author)
Django基礎(一)