Python的WEB框架有Django、Tornado、Flask 等多種,Django相較與其餘WEB框架其優點爲:大而全,框架自己集成了ORM、模型綁定、模板引擎、緩存、Session等諸多功能html
1、建立django程序python
其餘經常使用命令:mysql
python manage.py runserver 0.0.0.0
python manage.py startapp appname
python manage.py syncdb
python manage.py makemigrations
python manage.py migrategit
python manage.py createsuperuser程序員
2、程序目錄web
3、配置文件正則表達式
一、數據庫sql
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME':'dbname',
'USER': 'root',
'PASSWORD': 'xxx',
'HOST': '',
'PORT': '',
}
}
二、模版shell
TEMPLATE_DIRS = (
os.path.join(BASE_DIR,'templates'),
)
三、靜態文件數據庫
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "statics"),
os.path.join(BASE_DIR, "uploads"),
#'/var/www/static/',
#'/var/www/static2/',
]
一、每一個路由規則對應一個view中的函數
url(r'^index/(\d*)', views.index), url(r'^manage/(?P<name>\w*)/(?P<id>\d*)', views.manage), url(r'^manage/(?P<name>\w*)', views.manage,{'id':333}),
#
(?P<name>\w*) 是正則表達式的另外一種寫法,寫法規定(?p<名稱>模式)
二、根據app對路由規則進行一次分類
url(r'^web/',include('web.urls')),
三、url傳參
URLconf也有鉤,可讓你經過額外的參數到你的視圖函數,做爲一個Python字典。
from django.conf.urls import url from . import views urlpatterns = [ url(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive, {'foo': 'bar'}), ]
django中的路由系統和其餘語言的框架有所不一樣,在django中每個請求的url都要有一條路由映射,這樣才能將請求交給對一個的view中的函數去處理。其餘大部分的Web框架則是對一類的url請求作一條路由映射,從而是路由系統變得簡潔。
一、視圖的執行
視圖的建立過程,對於視圖,其實就是讀取視圖(其中嵌套着模版標籤),而後將 Model 中獲取的數據插入到視圖中,最後將信息返回給用戶。
實例
最簡單的返回一個字符串形式的view
from django.http import HttpResponse def my_view(request): if request.method == 'GET': # <view logic> return HttpResponse('result')
若是想直接返回一個html文檔
from django.shortcuts import render,HttpResponse
# Create your views here.
def test_view(request):
return render(request,'index.html')
更多...
def current_datetime(request): now = datetime.datetime.now() html = "<html><body>It is now %s.</body></html>" % now return HttpResponse(html)
from django import template
t = template.Template('My name is {{ name }}.')
c = template.Context({'name': 'Adrian'})
print t.render(c)
import datetime
from django import template
import DjangoDemo.settings
now = datetime.datetime.now()
fp = open(settings.BASE_DIR+'/templates/Home/Index.html')
t = template.Template(fp.read())
fp.close()
html = t.render(template.Context({'current_date': now}))
return HttpResponse(html)
from django.template.loader import get_template
from django.template import Context
from django.http import HttpResponse
import datetime
def current_datetime(request):
now = datetime.datetime.now()
t = get_template('current_datetime.html')
html = t.render(Context({'current_date': now}))
return HttpResponse(html)
return render_to_response('Account/Login.html',data,context_instance=RequestContext(request))
二、模版語言
模板中也有本身的語言,該語言能夠實現數據展現
{{ item }} {% for item in item_list %} <a>{{ item }}</a> {% endfor %} forloop.counter //相反的 forloop.first //判斷循環是否是第一個 forloop.last // 判斷循環是否是最後一個 {% if ordered_warranty %} {% else %} {% endif %} 母板:{% block title %}{% endblock %} 子板:{% extends "base.html" %} {% block title %}{% endblock %} 幫助方法: {{ item.event_start|date:"Y-m-d H:i:s"}} {{ bio|truncatewords:"30" }} {{ my_list|first|upper }} {{ name|lower }}
三、自定義simple_tag
a、在app中建立templatetags模塊
b、建立任意 .py 文件,如:xx.py
#!/usr/bin/env python #coding:utf-8 from django import template from django.utils.safestring import mark_safe from django.template.base import resolve_variable, Node, TemplateSyntaxError register = template.Library() @register.simple_tag def my_simple_time(v1,v2,v3): return v1 + v2 + v3 @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的html文件中導入以前建立的 xx.py 文件名
{% load xx %}
d、使用simple_tag
{% my_simple_time 1 2 3%} {% my_input 'id_username' 'hide'%}
e、在settings中配置當前app,否則django沒法找到自定義的simple_tag
INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01', )
到目前爲止,當咱們的程序涉及到數據庫相關操做時,咱們通常都會這麼搞:
django 自己提供了很是強大易使用的ORM組件,而且支持多種數據庫,如sqllite,mysql,progressSql,Oracle等,固然最經常使用的搭配仍是mysql,要啓用orm,先要配置好鏈接數據 的信息
在你建立的project目錄下編輯settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'OldboyWebsite', #確保此數據庫已存在
'HOST':'',
'PORT':'',
'USER':'root',
'PASSWORD':''
}
}
下面要開始學習Django ORM語法了,爲了更好的理解,咱們來作一個基本的 書籍/做者/出版商 數據庫結構。 咱們這樣作是由於 這是一個衆所周知的例子,不少SQL有關的書籍也經常使用這個舉例。
咱們來假定下面的這些概念、字段和關係:
一個做者有姓,有名及email地址。
出版商有名稱,地址,所在城市、省,國家,網站。
書籍有書名和出版日期。 它有一個或多個做者(和做者是多對多的關聯關係[many-to-many]), 只有一個出版商(和出版商是一對多的關聯關係[one-to-many],也被稱做外鍵[foreign key])
from django.db import models class Publisher(models.Model): name = models.CharField(max_length=30) address = models.CharField(max_length=50) city = models.CharField(max_length=60) state_province = models.CharField(max_length=30) country = models.CharField(max_length=50) website = models.URLField() class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField() class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField()
更多models field 字段:https://docs.djangoproject.com/en/1.9/ref/models/fields/
完成這些代碼以後,如今讓咱們來在數據庫中建立這些表。 要完成該項工做,第一步是在 Django 項目中 激活這些模型。 將上面的模型所在的app (此例子app是books) 添加到配置文件的已安裝應用列表中便可完成此步驟。
再次編輯 settings.py 文件, 找到 INSTALLED_APPS 設置。 INSTALLED_APPS 告訴 Django 項目哪些 app 處於激活狀態。 缺省狀況下以下所示:
INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'books', )
python manage.py makemigrations #生成同步紀錄 python manage.py migrate #開始同步
一旦你建立了模型,Django自動爲這些模型提供了高級的Python API。 運行 python manage.py shell 並輸入下面的內容試試看:
>>> from books.models import Publisher >>> p1 = Publisher(name='Apress', address='2855 Telegraph Avenue', ... city='Berkeley', state_province='CA', country='U.S.A.', ... website='http://www.apress.com/') >>> p1.save() >>> p2 = Publisher(name="O'Reilly", address='10 Fawcett St.', ... city='Cambridge', state_province='MA', country='U.S.A.', ... website='http://www.oreilly.com/') >>> p2.save() >>> publisher_list = Publisher.objects.all() >>> publisher_list [<Publisher: Publisher object>, <Publisher: Publisher object>]
這裏有一個值得注意的地方,在這個例子可能並未清晰地展現。 當你使用Django modle API建立對象時Django並未將對象保存至數據庫內,除非你調用`` save()`` 方法:
p1 = Publisher(...) # At this point, p1 is not saved to the database yet! p1.save() # Now it is.
若是須要一步完成對象的建立與存儲至數據庫,就使用`` objects.create()`` 方法。 下面的例子與以前的例子等價:
>>> p1 = Publisher.objects.create(name='Apress', ... address='2855 Telegraph Avenue', ... city='Berkeley', state_province='CA', country='U.S.A.', ... website='http://www.apress.com/') >>> p2 = Publisher.objects.create(name="O'Reilly", ... address='10 Fawcett St.', city='Cambridge', ... state_province='MA', country='U.S.A.', ... website='http://www.oreilly.com/') >>> publisher_list = Publisher.objects.all() >>> publisher_list
當咱們打印整個publisher列表時,咱們沒有獲得想要的有用信息,沒法把對象區分開來:
[<Publisher: Publisher object>, <Publisher: Publisher object>]
咱們能夠簡單解決這個問題,只須要爲Publisher 對象添加一個方法 __unicode__() 。 __unicode__() 方法告訴Python如何將對象以unicode的方式顯示出來。 爲以上三個模型添加__unicode__()方法後,就能夠看到效果了。
def __unicode__(self): return self.name
對__unicode__()的惟一要求就是它要返回一個unicode對象 若是`` __unicode__()`` 方法未返回一個Unicode對象,而返回好比說一個整型數字,那麼Python將拋出一個`` TypeError`` 錯誤,並提示:」coercing to Unicode: need string or buffer, int found」 。
>>> p = Publisher(name='Apress', ... address='2855 Telegraph Ave.', ... city='Berkeley', ... state_province='CA', ... country='U.S.A.', ... website='http://www.apress.com/') >>> p.save()
由於 Publisher 模型有一個自動增長的主鍵 id ,因此第一次調用 save() 還多作了一件事: 計算這個主鍵的值並把它賦值給這個對象實例:
接下來再調用 save() 將不會建立新的記錄,而只是修改記錄內容(也就是 執行 UPDATE SQL語句,而不是 INSERT語句):
>>> p.name = 'Apress Publishing' >>> p.save()
注意,並非只更新修改過的那個字段,全部的字段都會被更新。
固然,建立新的數據庫,並更新之中的數據是必要的,可是,對於 Web 應用程序來講,更多的時候是在檢索查詢數據庫。 咱們已經知道如何從一個給定的模型中取出全部記錄:
>>> Publisher.objects.all() [<Publisher: Apress>, <Publisher: O'Reilly>]
咱們不多會一次性從數據庫中取出全部的數據;一般都只針對一部分數據進行操做。 在Django API中,咱們可使用`` filter()`` 方法對數據進行過濾:
>>> Publisher.objects.filter(name='Apress') [<Publisher: Apress>]
你能夠傳遞多個參數到 filter() 來縮小選取範圍:
>>> Publisher.objects.filter(country="U.S.A.", state_province="CA") [<Publisher: Apress>]
在 name 和 contains 之間有雙下劃線。和Python同樣,Django也使用雙下劃線來代表會進行一些魔術般的操做。這裏,contains部分會被Django翻譯成LIKE語句:
>>> Publisher.objects.filter(name__contains="press") [<Publisher: Apress>]
其餘的一些查找類型有:icontains(大小寫不敏感的LIKE),startswith和endswith, 還有range
上面的例子中`` filter()`` 函數返回一個記錄集,這個記錄集是一個列表。 相對列表來講,有些時候咱們更須要獲取單個的對象, `` get()`` 方法就是在此時使用的:
>>> Publisher.objects.get(name="Apress") <Publisher: Apress>
這樣,就返回了單個對象,而不是列表(更準確的說,QuerySet)。 因此,若是結果是多個對象,會致使拋出異常:
>>> Publisher.objects.get(country="U.S.A.") Traceback (most recent call last): ... MultipleObjectsReturned: get() returned more than one Publisher -- it returned 2! Lookup parameters were {'country': 'U.S.A.'} 若是查詢沒有返回結果也會拋出異常: >>> Publisher.objects.get(name="Penguin") Traceback (most recent call last): ... DoesNotExist: Publisher matching query does not exist. 這個 DoesNotExist 異常 是 Publisher 這個 model 類的一個屬性,即 Publisher.DoesNotExist。在你的應用中,你能夠捕獲並處理這個異常,像這樣: try: p = Publisher.objects.get(name='Apress') except Publisher.DoesNotExist: print "Apress isn't in the database yet." else: print "Apress is in the database."
在運行前面的例子中,你可能已經注意到返回的結果是無序的。 咱們尚未告訴數據庫 怎樣對結果進行排序,因此咱們返回的結果是無序的。
在你的 Django 應用中,你或許但願根據某字段的值對檢索結果排序,好比說,按字母順序。 那麼,使用order_by() 這個方法就能夠搞定了。
>>> Publisher.objects.order_by("name") [<Publisher: Apress>, <Publisher: O'Reilly>]
咱們能夠對任意字段進行排序:
>>> Publisher.objects.order_by("address") [<Publisher: O'Reilly>, <Publisher: Apress>] >>> Publisher.objects.order_by("state_province") [<Publisher: Apress>, <Publisher: O'Reilly>]
若是須要以多個字段爲標準進行排序(第二個字段會在第一個字段的值相同的狀況下被使用到),使用多個參數就能夠了,以下:
>>> Publisher.objects.order_by("state_province", "address") [<Publisher: Apress>, <Publisher: O'Reilly>]
咱們還能夠指定逆向排序,在前面加一個減號 - 前綴:
>>> Publisher.objects.order_by("-name") [<Publisher: O'Reilly>, <Publisher: Apress>]
儘管很靈活,可是每次都要用 order_by() 顯得有點囉嗦。 大多數時間你一般只會對某些 字段進行排序。 在這種狀況下,Django讓你能夠指定模型的缺省排序方式:
class Publisher(models.Model): name = models.CharField(max_length=30) address = models.CharField(max_length=50) city = models.CharField(max_length=60) state_province = models.CharField(max_length=30) country = models.CharField(max_length=50) website = models.URLField() def __unicode__(self): return self.name class Meta: ordering = ['name']
如今,讓咱們來接觸一個新的概念。 class Meta,內嵌於 Publisher 這個類的定義中(若是 class Publisher 是頂格的,那麼 class Meta 在它之下要縮進4個空格--按 Python 的傳統 )。你能夠在任意一個 模型 類中使用Meta 類,來設置一些與特定模型相關的選項。Meta 還可設置不少其它選項,如今,咱們關注ordering 這個選項就夠了。 若是你設置了這個選項,那麼除非你檢索時特地額外地使用了 order_by(),不然,當你使用 Django 的數據庫 API 去檢索時,Publisher對象的相關返回值默認地都會按 name 字段排序。
咱們已經知道如何對數據進行過濾和排序。 固然,一般咱們須要同時進行過濾和排序查詢的操做。 所以,你能夠簡單地寫成這種「鏈式」的形式:
>>> Publisher.objects.filter(country="U.S.A.").order_by("-name") [<Publisher: O'Reilly>, <Publisher: Apress>]
另外一個經常使用的需求就是取出固定數目的記錄。 想象一下你有成千上萬的出版商在你的數據庫裏, 可是你只想顯示第一個。 你可使用標準的Python列表裁剪語句:
>>> Publisher.objects.order_by('name')[0] <Publisher: Apress>
相似的,你能夠用Python的range-slicing語法來取出數據的特定子集:
>>> Publisher.objects.order_by('name')[0:2]
注意,不支持Python的負索引(negative slicing):
>>> Publisher.objects.order_by('name')[-1] Traceback (most recent call last): ... AssertionError: Negative indexing is not supported.
雖然不支持負索引,可是咱們可使用其餘的方法。 好比,稍微修改 order_by() 語句來實現:
>>> Publisher.objects.order_by('-name')[0]
在「插入和更新數據」小節中,咱們有提到模型的save()方法,這個方法會更新一行裏的全部列。 而某些狀況下,咱們只須要更新行裏的某幾列。
例如說咱們如今想要將Apress Publisher的名稱由原來的」Apress」更改成」Apress Publishing」。若使用save()方法,如:
>>> p = Publisher.objects.get(name='Apress') >>> p.name = 'Apress Publishing' >>> p.save()
(注意在這裏咱們假設Apress的ID爲52)
Django的save()方法更新了不只僅是name列的值,還有更新了全部的列。 若name之外的列有可能會被其餘的進程所改動的狀況下,只更改name列顯然是更加明智的。 更改某一指定的列,咱們能夠調用結果集(QuerySet)對象的update()方法: 示例以下:
>>> Publisher.objects.filter(id=52).update(name='Apress Publishing')
update()方法對於任何結果集(QuerySet)均有效,這意味着你能夠同時更新多條記錄。 如下示例演示如何將全部Publisher的country字段值由’U.S.A’更改成’USA’:
>>> Publisher.objects.all().update(country='USA')
2
update()方法會返回一個整型數值,表示受影響的記錄條數。 在上面的例子中,這個值是2。
刪除數據庫中的對象只需調用該對象的delete()方法便可:
>>> p = Publisher.objects.get(name="O'Reilly") >>> p.delete() >>> Publisher.objects.all() [<Publisher: Apress Publishing>]
一樣咱們能夠在結果集上調用delete()方法同時刪除多條記錄。這一點與咱們上一小節提到的update()方法類似:
>>> Publisher.objects.filter(country='USA').delete() >>> Publisher.objects.all().delete() >>> Publisher.objects.all() []
刪除數據時要謹慎! 爲了預防誤刪除掉某一個表內的全部數據,Django要求在刪除表內全部數據時顯示使用all()。 好比,下面的操做將會出錯:
>>> Publisher.objects.delete() Traceback (most recent call last): File "<console>", line 1, in <module> AttributeError: 'Manager' object has no attribute 'delete'
而一旦使用all()方法,全部數據將會被刪除:
>>> Publisher.objects.all().delete()
若是隻須要刪除部分的數據,就不須要調用all()方法。再看一下以前的例子:
>>> Publisher.objects.filter(country='USA').delete()
你可能已經注意到咱們在例子視圖中返回文本的方式有點特別。 也就是說,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)來實現這種模式,這就是本章要具體討論的問題。
Django 模版基本語法
>>> from django.template import Context, Template >>> t = Template('My name is {{ name }}.') >>> c = Context({'name': 'Stephane'}) >>> t.render(c) u'My name is Stephane.'
>>> from django.template import Template, Context >>> t = Template('Hello, {{ name }}') >>> print t.render(Context({'name': 'John'})) Hello, John >>> print t.render(Context({'name': 'Julie'})) Hello, Julie >>> print t.render(Context({'name': 'Pat'})) Hello, Pat
不管什麼時候咱們均可以像這樣使用同一模板源渲染多個context,只進行 一次模板建立而後屢次調用render()方法渲染會更爲高效:
Django 模板解析很是快捷。 大部分的解析工做都是在後臺經過對簡短正則表達式一次性調用來完成。 這和基於 XML 的模板引擎造成鮮明對比,那些引擎承擔了 XML 解析器的開銷,且每每比 Django 模板渲染引擎要慢上幾個數量級。
在到目前爲止的例子中,咱們經過 context 傳遞的簡單參數值主要是字符串,然而,模板系統可以很是簡潔地處理更加複雜的數據結構,例如list、dictionary和自定義的對象。
在 Django 模板中遍歷複雜數據結構的關鍵是句點字符 (.)。
最好是用幾個例子來講明一下。 好比,假設你要向模板傳遞一個 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) u'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) u'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) u'Hello, John Smith.'
點語法也能夠用來引用對象的* 方法*。 例如,每一個 Python 字符串都有 upper() 和 isdigit() 方法,你在模板中可使用一樣的句點語法來調用它們:
>>> from django.template import Template, Context >>> t = Template('{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}') >>> t.render(Context({'var': 'hello'})) u'hello -- HELLO -- False' >>> t.render(Context({'var': '123'})) u'123 -- 123 -- True'
注意這裏調用方法時並* 沒有* 使用圓括號 並且也沒法給該方法傳遞參數;你只能調用不需參數的方法。 (咱們將在本章稍後部分解釋該設計觀。)
最後,句點也可用於訪問列表索引,例如:
>>> from django.template import Template, Context >>> t = Template('Item 2 is {{ items.2 }}.') >>> c = Context({'items': ['apples', 'bananas', 'carrots']}) >>> t.render(c) u'Item 2 is carrots.'
在講解了模板加載機制以後,咱們再介紹一個利用該機制的內建模板標籤: {% include %} 。該標籤容許在(模板中)包含其它的模板的內容。 標籤的參數是所要包含的模板名稱,能夠是一個變量,也能夠是用單/雙引號硬編碼的字符串。 每當在多個模板中出現相同的代碼時,就應該考慮是否要使用 {% include %} 來減小重複。
下面這兩個例子都包含了 nav.html 模板。這兩個例子是等價的,它們證實單/雙引號都是容許的。
{% include 'nav.html' %} {% include "nav.html" %}
下面的例子包含了 includes/nav.html 模板的內容:
{% include 'includes/nav.html' %}
到目前爲止,咱們的模板範例都只是些零星的 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 %} 標籤中的內容老是被看成一條退路。
繼承並不會影響到模板的上下文。 換句話說,任何處在繼承樹上的模板均可以訪問到你傳到模板中的每個模板變量。
你能夠根據須要使用任意多的繼承次數。 使用繼承的一種常見方式是下面的三層法:
建立 base.html 模板,在其中定義站點的主要外觀感覺。 這些都是不常修改甚至從不修改的部分。
爲網站的每一個區域建立 base_SECTION.html 模板(例如, base_photos.html 和 base_forum.html )。這些模板對base.html 進行拓展,幷包含區域特定的風格與設計。
爲每種類型的頁面建立獨立的模板,例如論壇頁面或者圖片庫。 這些模板拓展相應的區域模板。
這個方法可最大限度地重用代碼,並使得向公共區域(如區域級的導航)添加內容成爲一件輕鬆的工做。
如下是使用模板繼承的一些訣竅:
若是在模板中使用 {% extends %} ,必須保證其爲模板中的第一個模板標記。 不然,模板繼承將不起做用。
通常來講,基礎模板中的 {% block %} 標籤越多越好。 記住,子模板沒必要定義父模板中全部的代碼塊,所以你能夠用合理的缺省值對一些代碼塊進行填充,而後只對子模板所需的代碼塊進行(重)定義。 俗話說,鉤子越多越好。
若是發覺本身在多個模板之間拷貝代碼,你應該考慮將該代碼段放置到父模板的某個 {% block %} 中。
若是你須要訪問父模板中的塊的內容,使用 {{ block.super }}這個標籤吧,這一個魔法變量將會表現出父模板中的內容。 若是隻想在上級代碼塊基礎上添加內容,而不是所有重載,該變量就顯得很是有用了。
不容許在同一個模板中定義多個同名的 {% block %} 。 存在這樣的限制是由於block 標籤的工做方式是雙向的。 也就是說,block 標籤不只挖了一個要填的坑,也定義了在父模板中這個坑所填充的內容。若是模板中出現了兩個相同名稱的 {% block %} 標籤,父模板將無從得知要使用哪一個塊的內容。