Django基礎三之路由、檢視、模板

Hans_Wang發表於2022-03-16

Django基礎三之路由、檢視、模板

1. Django 請求和返回週期

Django預設使用wsgiref模組但是該模組併發量特別小(大約1000),不適用於線上環境,所以在Django專案上線之後會使用uwsgi

image

1.1 路由層之路由匹配

主要是在ursl.py檔案裡書寫。

1.11版本:
urlpatterns = [
    url('^admin/', admin.site.urls),
]

3.2版本:
urlpatterns = [
    path('admin/', admin.site.urls),
    path('test/', views.test),
    path('testadd/', views.testadd),
]
1版本中使用url方法:
url()方法:
1,第一個引數為一個正則
2,只要能匹配上就會執行後面的檢視函式

3版本中使用path
path()方法
第一個引數是一個字串
如果使用正則,則要使用 re_path() 而不是 path() 。
urlpatterns = [
 	re_path(r'^admin/', admin.site.urls),
]

test/和testadd/ 在匹配的時候如果不寫後面的斜槓(/),發現也能匹配上,是因為Django在做的時候如果test匹配不上,它會讓瀏覽器後面自動加上斜槓(/)再試一次。
這個是用settings裡面的APPEND_SLASH引數控制,預設為True,如果只想匹配一次則設定為False.
APPEND_SLASH=False

Django3.x在匹配時有了路徑轉換器:

  • str - 匹配除了 '/' 之外的非空字串。如果表示式內不包含轉換器,則會預設匹配字串。

  • int - 匹配 0 或任何正整數。返回一個 int

       path('articles/<int:year>/', views.year_archive),
        <int:year>是個整型引數
    
  • slug - 匹配任意由 ASCII 字母或數字以及連字元和下劃線組成的短標籤。比如,building-your-1st-django-site

  • uuid - 匹配一個格式化的 UUID 。為了防止多個 URL 對映到同一個頁面,必須包含破折號並且字元都為小寫。比如,075194d3-6885-417e-a8a8-6c931e272f00。返回一個 UUID 例項。

  • path - 匹配非空欄位,包括路徑分隔符 '/' 。它允許你匹配完整的 URL 路徑而不是像 str 那樣匹配 URL 的一部分。

1.2 有名分組

命名正規表示式組的語法是 (?P<name>pattern) 其中 name 是組名,pattern 是要匹配的模式

在Django3中路由匹配使用正則:
ursl.py檔案:
from django.contrib import admin
from django.urls import path,re_path #要手動匯入re_path

from orm import  views
urlpatterns = [


    path('admin/', admin.site.urls),
    path('test/', views.test),
    path('testadd/', views.testadd),
    re_path(r'test/(?P<year>[0-9]{4})/', views.testadd),

]


在views.py:
def testadd(request,year):
    print(year)
    return  HttpResponse("from test")

// 分組名必須要傳給後面的檢視函式,否則會報錯。
如上面的例子,分組名為year,如果不傳給後端的views.testadd函式,報錯資訊:
    testadd() got an unexpected keyword argument 'year'
    
有名分組
將括號內正規表示式匹配到的內容當做關鍵字引數傳遞給後面的檢視函式


1.3 無名分組

有命名組語法,例如 (?P<year>[0-9]{4}) ,你也可以使用更短的未命名組,例如 ([0-9]{4})

在Django3中路由匹配使用正則:
ursl.py檔案:
from django.contrib import admin
from django.urls import path,re_path #要手動匯入re_path

from orm import  views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('test/', views.test),
    path('testadd/', views.testadd),
    re_path(r'test/([0-9]{4})/$', views.test),
]


啟動訪問:
    http://127.0.0.1:8000/test/1234/
報錯:
    test() takes 1 positional argument but 2 were given
解決方法:
在views.py:
def test(request,what):
    print(what)
    return  HttpResponse("from test")

再執行訪問成功。
控制檯列印的結果:
1234

無名分組:
	將括號內正規表示式匹配到的內容當做位置引數傳遞給後面的檢視函式。
    

總結:

  1. 有名分組和無名分組不能混合使用。
  2. 單個種類可以重複使用

2. 反射解析

當路由頻繁變化的時候,HTML介面上的連線地址如何做到動態解析。

"""
1. 給路由與檢視函式對應關係新增一個別名(名字自己定義,名字之間不要衝突)
path('show/', views.show, name='showtime'),

2. 根據這個別名動態解析出一個結果,該結果可以直接訪問到對應的路由
	前端使用別名:
	<a href="{% url 'showtime' %}"><h1>Hello Django</h1></a>
	這樣不管path裡面的show怎麼變,只要name='showtime'不變,那麼訪問就沒問題
    
	後端使用別名:
	ursl.py:
	urlpatterns = [
	path('show/', views.show, name='showtime'),
	]
	
	views.py
	from django.shortcuts import render, HttpResponse,redirect,reverse
	def delete(request):
		......
		print(reverse('showtime')) # 列印url
		return redirect('showtime') # 也可以直接在重定向裡寫別名

"""

無名和有名分組指向解析

ursl.py
"""
from django.urls import path,re_path
urlpatterns = [
	re_path(r'test/([0-9]{4})/$', views.test, name='index_test'),
]
"""
views.py
"""
def delete(request):
		......
		print(reverse('index_test',args=(1,))) # 列印url
		
args=(1,) args後面跟一個元組,裡面這寫的是1,推薦寫主鍵的值	
r'test/([0-9]{4})/([0-9]{4})/$ 如果有兩個分組,則args後面必須寫兩個值,(1,2)第二個值可以隨便寫
"""

前端也一樣:
<a href="{% url 'index_test' 123 %}"><h1>Hello Django</h1></a>
這裡123也是隨便寫的,只要寫個數字就行




有名:
後端
reverse('index_test',kwargs={'id':123})
前端
<a href="{% url 'index_test' id=123 %}"><h1>Hello Django</h1></a>

總結

無名和有名都可以使用一種(無名)反向解析的形式

3. 路由分發

其實Django中的每一個應用都可以有自己的urls.pystatic資料夾、templates資料夾,這樣使用Django做分組開發非常的簡便。每個人只需要寫息的應用即可,最後彙總到一個空的Django專案中然後使用路由分發將多個應用關聯。

image

示例:

建立一個專案,並建立兩個應用(app01,app02).
在每個應用裡面都建立一個urls.py檔案。
app01 urls.py:
"""

from django.urls import path
from app01 import  views
urlpatterns = [
    path('index', views.index),
]
"""
app01 views.py:

"""
from django.shortcuts import render,HttpResponse

# Create your views here.

def index(request):
    return HttpResponse("from app01 index")

"""

app02 urls.py:
"""
from django.urls import path
from app02 import  views
urlpatterns = [
    path('index', views.index),
]
"""
app02 views.py:
"""
from django.shortcuts import render,HttpResponse

# Create your views here.

def index(request):
    return HttpResponse("from app02 index")

"""

專案中總的urls.py:
"""
from django.contrib import admin
from django.urls import path,include
# 匯入應用的urls
from app01 import urls as app01_urls
from app02 import urls as app02_urls

urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/', include(app01_urls)),
    path('app02/', include(app02_urls)),

]
"""
注意:
需要在總的urls.py裡匯入include
from django.urls import path,include
在總的路由裡面不能加$符號,否則沒辦法分發


還有一種在總的urls.py裡不需要匯入應用urlsr 的方法:
    
專案中總的urls.py: 
"""
from django.contrib import admin
from django.urls import path,include


urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/', include('app01.urls')),
    path('app02/', include('app02.urls')),

]
"""

4 名稱空間

當多個應用在反射解析的時候如果出現了別名衝突的情況,那麼將會無法自動識別

示例:

app01 urls.py:
"""
from django.urls import path
from app01 import  views
urlpatterns = [
    path('index', views.index,name='index_name'),
    path('login', views.login)
]
"""
app01 views.py:
"""
from django.shortcuts import render,HttpResponse,reverse

# Create your views here.

def index(request):
    return HttpResponse("from app01 index")

def login(request):
    print(reverse('index_name'))
    return HttpResponse("from app01 login")
"""
app02 urls.py:
"""

from django.urls import path
from app02 import  views
urlpatterns = [
    path('index', views.index,name='index_name'),
    path('login', views.login),
]
"""
app02 views.py:
"""
from django.shortcuts import render,HttpResponse,reverse

# Create your views here.

def index(request):
    return HttpResponse("from app02 index")

def login(request):
    print(reverse('index_name'))
    return HttpResponse("from app02 login")
"""

專案中總的urls.py: 
"""

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

urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/', include('app01.urls')),
    path('app02/', include('app02.urls')),

]
"""
雖然訪問頁面:
http://127.0.0.1:8000/app01/login
http://127.0.0.1:8000/app02/login
的時候能正常拿到對應的頁面,但是在後端發現拿到的是同一個:
/app02/index
/app02/index

要解決這個問題就用到了名稱空間

解決方法一:使用名稱空間

在總路上加上namespace這個引數:
專案中總的urls.py: 
"""

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

urlpatterns = [
    path('admin/', admin.site.urls),
	path('app01/', include('app01.urls',namespace='app01')),
    path('app02/', include('app02.urls',namespace='app02')),

]
"""

app01 urls.py:
"""
from django.urls import path
from app01 import  views
app_name='app01'
urlpatterns = [
    path('index', views.index,name='index_name'),
    path('login', views.login)
]
"""
app01 views.py:
"""
from django.shortcuts import render,HttpResponse,reverse

# Create your views here.

def index(request):
    return HttpResponse("from app01 index")

def login(request):
    print(reverse('app01:index_name'))
    return HttpResponse("from app01 login")
"""
app02 urls.py:
"""

from django.urls import path
from app02 import  views
app_name='app02'
urlpatterns = [
    path('index', views.index,name='index_name'),
    path('login', views.login),
]
"""
app02 views.py:
"""
from django.shortcuts import render,HttpResponse,reverse

# Create your views here.

def index(request):
    return HttpResponse("from app02 index")

def login(request):
    print(reverse('app02:index_name'))
    return HttpResponse("from app02 login")
"""

訪問頁面:
http://127.0.0.1:8000/app01/login
http://127.0.0.1:8000/app02/login
拿到的就是
/app01/index
/app02/index


注意在Django3.2版本中使用名稱空間的時候,一定要給每個應用設定應用名,否則會報錯:
'''pecifying a namespace in include() without providing an app_name is not supported. Set the app_name attribute in the included module, or pass a 2-tuple containing the list of patterns and app_name instead.'''
解決方法:
app01 urls.py:
'''
app_name='app01'
'''

app02 urls.py:
'''app_name='app02''''
這兩個必須要設定。

前端使用名稱空間:

<a href="{% url 'app01:index_name' %}">app01_index</a>
<a href="{% url 'app02:index_name' %}">app02_index</a>

注意:

雖然我們現在可以將模板檔案直接放在 app01/templates 資料夾中(而不是再建立一個 app01 子資料夾),但是這樣做不太好。Django 將會選擇第一個匹配的模板檔案,如果你有一個模板檔案正好和另一個應用中的某個模板檔案重名,Django 沒有辦法 區分 它們。我們需要幫助 Django 選擇正確的模板,最好的方法就是把他們放入各自的 名稱空間 中,也就是把這些模板放入一個和 自身 應用重名的子資料夾裡。(app01/templates/app01/login.html)

同理:多個應用下的靜態檔案也是這樣。

所以在前端使用名稱空間的時候,HTML檔案的路徑為:

app01/templates/app01/login.html
app02/templates/app02/login.html

後端app01 views.py:
from django.shortcuts import render,HttpResponse,reverse
def login(request):
    print(reverse('app01:index_name'))
 	return render(request, "app01/login.html")

後端app02 views.py:
from django.shortcuts import render,HttpResponse,reverse
def login(request):
    print(reverse('app02:index_name'))
    return render(request, "app02/login.html")

解決方法二:別名別衝突

寫別名的時候要加上自己應用名做字首。

5. JsonResponse

給前端返回一個json格式的資料

方法一:自己序列化

views.py:    
from django.shortcuts import render,HttpResponse,reverse
import json

def index(request):
    d = {'user':'Hans', 'password':123}
    d_json = json.dumps(d)
    return HttpResponse(d_json)

# json預設不能直接識別的字元直接返回對應的unicode編碼,如上面的漢字要正確返回則需要設定ensure_ascii=False
d = {'user':'Hans你好', 'password':123}
d_json = json.dumps(d,ensure_ascii=False)

方法二: 使用JsonResponse

views.py:
    
from django.shortcuts import render,HttpResponse,reverse
from django.http import JsonResponse

def index(request):
    d = {'user':'Hans', 'password':123}
    return JsonResponse(d)

# JsonResponse 對不能識別的字元也是直接返回unicode編碼,如果對漢字也能正常展示,加上json_dumps_params={'ensure_ascii':False}引數:
d = {'user':'Hans你好', 'password':123}
return JsonResponse(d,json_dumps_params={'ensure_ascii':False})

# 如果序列化一個非字典型別的,則需要讓safe=False

如:
li = ['A','B','C']
return JsonResponse(d, safe=Fasle)

6. 上傳檔案

前端頁面:

    <form action="" method="post" enctype="multipart/form-data" class="form-control">
        <p><input type="file" name="files"></p>
        <p><input type="submit" value="提交" ></p>

    </form>

# 路由層:
path('upfile', views.upfile),
# 檢視層
views.py:
from django.shortcuts import render,HttpResponse,reverse

def upfile(request):
    if request.method == 'POST':
        file_obj = request.FILES.get('files')
        print(file_obj.name)
        with open(r'./app01/templates/%s' % file_obj.name, 'wb') as f:
            for chunk in file_obj.chunks():
                f.write(chunk)
    return render(request,"app01/upfile.html")

7. FBV和CBV

FBV:基於函式的檢視

CBV:基於類的檢視

上面寫的都為FBV,基於函式的檢視,現在寫一個基於類的檢視。

# views.py
from django.shortcuts import render,HttpResponse,reverse
from django.views import View

class MyView(View):
    def get(self,request):
        return HttpResponse("GET方法")
    def pos(self,request):
        return HttpResponse("POST方法")
    
# urls.py
from django.urls import path
from . import  views

urlpatterns = [
    path('myview', views.MyView.as_view()),
]

#CBV和FBV路由匹配其實是一樣的。

8. 模板語法傳值

8.1 傳基本資料型別

# 方法一:精確傳值

# urls.py
""" 
from django.contrib import admin
from django.urls import path

from    templateByValue import  views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('index/', views.index),
]
""" 
# 前端HTML:
""" 
<body>
<ul>{{ i }}</ul>
<ul>{{ f }}</ul>
<ul>{{ str }}</ul>
<ul>{{ Li }}</ul>
<ul>{{ set01 }}</ul>
<ul>{{ t }}</ul>
<ul>{{ bool_value }}</ul>
<ul>{{ d }}</ul>
</body>
""" 

views.py:
"""   
from django.shortcuts import render

# Create your views here.

def index(request):
    i = 123
    f = 12.3
    str = "Hello Django"
    Li = [1, 2, 3]
    d = {'username':"Hans", "age":19}
    t = (1, 2, 3, 4)
    set01 = {1, 2, 3, 4}
    bool_value = True

    return render(request,'index.html',{"i":i,"f":f,"str":str,"Li":Li,"d":d,'t':t, "set01":set01,'bool_value':bool_value})

""" 

# 方法二:使用locals函式
# 在views.py 中給前端頁面傳值每個都要寫,在值特別多的時候不方便,可以使用locals函式
"""
    return render(request,"index.html",locals())
"""
locals() 獲取全部區域性變數:
{'request': <WSGIRequest: GET '/index/'>, 'i': 123, 'f': 12.3, 'str': 'Hello Django', 'Li': [1, 2, 3], 'd': {'username': 'Hans', 'age': 19}, 't': (1, 2, 3, 4), 'set01': {1, 2, 3, 4}, 'bool_value': True},然後全部傳給前端頁面。

兩者的優缺點:
    方法一,可以精確傳值,不會造成資源浪費,但傳的值多的時候書寫不方便
    方法二, 書寫方便,但是會造成資源浪費。

8.2 傳函式名

# 前端:
"""
<body>
<p>{{ foo }}</p>
</body>
"""
# views.py:
"""
from django.shortcuts import render

# Create your views here.

# 定義函式
def index(request):
	def foo():
        print("hello")
        return  "Hello Django"
    return render(request,"index.html",{"foo":foo}) # 給前端傳遞,前面拿到的是函式的返回值。
"""
使用模板語法傳函式的時候,不支援帶引數

8.3 傳類名

# 前端:
"""
<body>
<p>{{ MyClass }}</p>
<p>{{ obj }}</p>
<p>{{ obj.get_self }}</p>
<p>{{ obj.get_cls }}</p>
<p>{{ obj.get_static }}</p>
</body>
"""
#views.py
"""
from django.shortcuts import render

# Create your views here.

def index(request):
    class MyClass(object):
        def get_self(self):
            return "繫結給物件的方法"
        @classmethod
        def get_cls(cls):
            return "繫結給類的方法"
        @staticmethod
        def get_static():
            return "普通的函式"
    obj = MyClass()
    print(locals())
    return render(request,"index.html",{"MyClass":MyClass,"obj":obj})
"""
或直接寫:
return render(request,"index.html",locals())

總結

傳遞函式名和類名都會自動加括號呼叫(模板語法不支援額外的傳引數)

9. 模板語法獲取值

Django中模板語法取值只用.

# views.py
"""
from django.shortcuts import render

# Create your views here.

def index(request):
    Li = [1, 2, 3]
    d = {'username':"Hans", "age":19}

    return render(request,'index.html',locals())

""" 
# 前端:
"""
<ul>{{ Li.1}</ul>  拿列表第二個值
<ul>{{ set01.age}}</ul>  拿年齡
"""

10. 模板語法過濾器

過濾器的符號是管道符:|,將管道符左側的資料當做第一個引數。

# views.py:
"""
from django.shortcuts import render

# Create your views here.

def index(request):
    i = 123
    str = "Hello Django"
    Li = [1, 2, 3]
    d = {'username':"Hans", "age":19}
    bool_value = True
    bool_var = False
    import datetime
    ctime = datetime.datetime.now()
    file_size = 409600
    h = "<h1>Hello</h1>"
    from django.utils.safestring import mark_safe
    h1 =mark_safe("<h1>Django</h1>")   #後端也可以直接寫HTML語法返回給前端了
    return render(request,"index.html",locals())

"""

# 前端:
"""
<body>
<p>過濾器:將管道符左側的資料當做第一個引數</p>
<p>統計長度:{{ str|length }}</p>
<p>加法:{{ i|add:10000 }}</p>
<p>字串拼接:{{ str|add:"HAHA" }}</p>
<p>日期格式:{{ ctime|date:"Y-m-d" }}</p>
<p>預設值:{{ bool_value|default:"哈哈" }}</p>
<p>預設值:{{ bool_var|default:"哈哈" }}</p>
<p>檔案大小:{{ file_size|filesizeformat }}</p>
<p>擷取文字(截6個字元,包括3個點):{{ str|truncatechars:6 }}</p>
<p>擷取文字(截1個單詞,不包括3個點):{{ str|truncatewords:1 }}</p>
<p>h源資訊:{{ h }}</p>
<p>前端把後端傳過來的資料(h),格式成HTML格式: {{ h|safe }}</p>
<p>後端傳過來的資料(h1)直接為HTML格式顯示: {{ h1 }}</p>
</body>

"""

image

11. 模板語法標籤(流程控制)

# if 
{% if var %}
    <p>good</p>
{% endif  %}

# if else
{% if bool_var %}
    <p>var</p>
{% else %}
    <p>valu</p>
{% endif %}

# if ...  elif ... else   

{% if bool_var %}
    <p>var</p>
{% elif bool_value %}
    <p>value</p>
{% else %}
    <p>都沒有</p>
{% endif %}


# for

{% for foo in Li %}
    <p>foo</p>
{% endfor %}

# for內可以巢狀if
{% for foo in Li %}
    <p>foo</p>
    {% empty %}  # 如果是空的時候,列印empty裡的
    	<p>空值</p>
{% endfor %}


{{}} 變數相關的用
{%%} 邏輯相關的用


# with  
{% with d.3.username as name %} # 給d.3.username起別名
    {{ name }}
{% endwith %}

這個別名只能在with裡面用。

12. 自定義過濾器、標籤、inclusion_tag

類似於python裡面的自定義函式

1. 在應用下建立一個名字必須叫"templatetags"資料夾
2, 在上述資料夾內建立一個任意名稱的py檔案
3, 在該py檔案內固定寫入:
	from django import template
    register = template.Library()

12.1 自定義過濾器:

示例:

1,在應用下建立templatetags資料夾
2,在templatetags夾裡建立myFilter.py
檔案內容:
"""
from django import template

register = template.Library()

@register.filter(name="My") # 過濾器名
def index(a,b):
    return a+b
"""
3, views.py
from django.shortcuts import render
def index(request):
    i = 123
    return render(request,"index.html",locals())

4,前端頁面:
    <div>
        {% load myFilter %}
    	<p>{{ i |My:100}}</p>
    </div>
    
5. 瀏覽器顯示結果:
223

過濾器只能接受兩個引數。

12.2 自定義標籤

1, 依然是在myFilter.py檔案裡:
"""
from django import template

register = template.Library()

@register.simple_tag(name='myTag') # 標籤名
def foo(a,b,c,d):
    return "{%s:%s  %s:%s}" % (a,b,c,d)
"""
2, 前端頁面:
"""
<div>
	 {% load myFilter %}
    {% myTag 1 "hans" 2 "Hello" %} # 標籤傳值使用空格分隔
</div>
"""

標籤可以接受多個引數

12.3 自定義inclusion_tag

前面自定義的過濾器和標籤,都是自定義的過濾器函式和標籤函式直接返回給前端,自定義inclusion_tag有些不同。

image

在myFilter.py檔案裡:
from django import template

register = template.Library()    

@register.inclusion_tag('login.html', name="myInclusion") # inclusion_tag名字 
def foo2(n):
    l =[]
    for i in range(1, n+1):
        l.append("第%s頁" % i)
    return locals()

# login.html
<ul>
    {% for foo in l %}
        <li>{{ foo }}</li>
    {% endfor %}
</ul>

# 前端頁面:
<div>
    {% load myFilter %}
    {% myInclusion 4 %}
</div>

結果:

    第1頁
    第2頁
    第3頁
    第4頁

總結:

前端要使用自定義過濾器,標籤和inclusion_tag都先要load.

在某個區域需要反覆使用並且資料不固定,適合使用inclusion_tag.

13. 模板的匯入

類似於python導模組

例如有一個頁面會經常用到,不可能每次用到就寫一份,可以使用模板匯入的方法。

頁面匯入模板關鍵字:{%include%}

經常用到的頁面form.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <form>
              <div class="form-group">
                <label for="exampleInputEmail1">Email address</label>
                <input type="email" class="form-control" id="exampleInputEmail1" placeholder="Email">
              </div>
              <div class="form-group">
                <label for="exampleInputPassword1">Password</label>
                <input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password">
              </div>
              <button type="submit" class="btn btn-default">Submit</button>
            </form>
        </div>

    </div>

</div>
</body>
</html>

模板匯入:

需要用到模板的頁面:
index.html
<body>
    <div>
        {% include 'form.html' %}
    </div>
    
</body>

14. 模板的繼承

image

示例:

主頁home.html(模板)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
    <div class="row">
        <nav class="navbar navbar-inverse">
          <div class="container-fluid">
            <!-- Brand and toggle get grouped for better mobile display -->
            <div class="navbar-header">
              <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
              </button>
              <a class="navbar-brand" href="#">Brand</a>
            </div>

            <!-- Collect the nav links, forms, and other content for toggling -->
            <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
              <ul class="nav navbar-nav">
                <li class="active"><a href="#">Link <span class="sr-only">(current)</span></a></li>
                <li><a href="#">Link</a></li>
                <li class="dropdown">
                  <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>
                  <ul class="dropdown-menu">
                    <li><a href="#">Action</a></li>
                    <li><a href="#">Another action</a></li>
                    <li><a href="#">Something else here</a></li>
                    <li role="separator" class="divider"></li>
                    <li><a href="#">Separated link</a></li>
                    <li role="separator" class="divider"></li>
                    <li><a href="#">One more separated link</a></li>
                  </ul>
                </li>
              </ul>
              <form class="navbar-form navbar-left">
                <div class="form-group">
                  <input type="text" class="form-control" placeholder="Search">
                </div>
                <button type="submit" class="btn btn-default">Submit</button>
              </form>
              <ul class="nav navbar-nav navbar-right">
                <li><a href="#">Link</a></li>
                <li class="dropdown">
                  <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>
                  <ul class="dropdown-menu">
                    <li><a href="#">Action</a></li>
                    <li><a href="#">Another action</a></li>
                    <li><a href="#">Something else here</a></li>
                    <li role="separator" class="divider"></li>
                    <li><a href="#">Separated link</a></li>
                  </ul>
                </li>
              </ul>
            </div><!-- /.navbar-collapse -->
          </div><!-- /.container-fluid -->
        </nav>
    <!--左側-->
        <div class="list-group col-md-2">
          <a href="#" class="list-group-item active">
            Cras justo odio
          </a>
          <a href="/compute/" class="list-group-item">電腦</a>
          <a href="/phone/" class="list-group-item">手機</a>
          <a href="/beauty/" class="list-group-item">beauty</a>
          <a href="#" class="list-group-item">Vestibulum at eros</a>
        </div>
    <!--右側-->
        {% block content %}
        <div class="jumbotron col-md-10">
          <h1>Hello, world!</h1>
          <p>...</p>
          <p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a></p>
        </div>
        {% endblock %}
    </div>
</div>
</body>
</html>

<!--
{% block content %}  
這個區域做了標記,這個區域是可以修改的。
content 這個名字可以隨意起
{% endblock %}
-->

電腦(compute.html)頁面繼承home.html

{% extends 'home.html' %}
{% block content %}
<div class="row">
  <div class="col-xs-6 col-md-3">
    <a href="https://www.apple.com.cn/shop/buy-mac/macbook-pro/MK1A3CH/A" class="thumbnail">
      <img src="https://store.storeimages.cdn-apple.com/8756/as-images.apple.com/is/mbp16-spacegray-gallery1-202110_GEO_CN?wid=4000&hei=3072&fmt=jpeg&qlt=80&.v=1633656602000">

    </a>
  </div>
</div>
{% endblock %}


<!--
{% extends 'home.html' %} 繼承home.html
{% block content %}
這個區域寫homecompute自己的內容
{% endblock %}
-->

手機(phone.html)頁面繼承home.html

{% extends 'home.html' %}
{% block content %}
<div class="row">
  <div class="col-xs-6 col-md-3">
    <a href="#" class="thumbnail">
      <img src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fexp-picture.cdn.bcebos.com%2F27725684cde34b2ca90aafca0d0e7c75e4f4437e.jpg%3Fx-bce-process%3Dimage%2Fresize%2Cm_lfit%2Cw_500%2Climit_1&refer=http%3A%2F%2Fexp-picture.cdn.bcebos.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1648558900&t=4b7021e3f90edeb7bf92842b1df965cb
">
    </a>
  </div>
</div>
{% endblock %}

image

image

子模板不但能修改被標記的位置,還可以使用模板內容:

{% extends 'home.html'%}
{% block content %}
<div class="row">
    <div class="col-xs-6 col-md-3">
         <a href="#" class="thumbnail">
        <img src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.jj20.com%2Fup%2Fallimg%2F4k%2Fs%2F02%2F2110021F21V024-0-lp.jpg&refer=http%3A%2F%2Fimg.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1648559897&t=c5b2a29fac9a42cb0f7019ea13b40d21">
         </a>
    </div>
</div>
    {{ block.super }}
{% endblock %}

<!--
 {{ block.super }} 為模板內被標記的區域
-->

模板在標記區域的時候一般有三個區域

  1. CSS區域
  2. HTML區域
  3. JS區域

目的是為了讓繼承的子模板具有獨立的CSS和JS,增加擴充套件性

<head>
   {% balock css %}
    css 樣式
   {% endblock %}
</head>

<body>
   {% balock html %}
    html內容
   {% endblock %}
    
   {% balock js %}
    js 內容
   {% endblock %}
</body>
子板也可以使用模板標記的區域的內容:
 {{ block.super }} 

相關文章