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

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),
]


啟動訪問:
    //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')),

]
"""
雖然訪問頁面:
//127.0.0.1:8000/app01/login
//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")
"""

訪問頁面:
//127.0.0.1:8000/app01/login
//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="//cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <link href="//cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="//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="//cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <link href="//cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="//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="//www.apple.com.cn/shop/buy-mac/macbook-pro/MK1A3CH/A" class="thumbnail">
      <img src="//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="//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="//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 }} 
Tags: