Django基础三之路由、视图、模板

1. Django 请求和返回周期

Django默认使用wsgiref模块但是该模块并发量特别小(大约1000),不适用于线上环境,所以在Django项目上线之后会使用uwsgi

Django基础三之路由、视图、模板-LMLPHP

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在匹配时有了路径转换器:

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项目中然后使用路由分发将多个应用关联。

Django基础三之路由、视图、模板-LMLPHP

示例:

创建一个项目,并创建两个应用(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>

注意:

所以在前端使用名称空间的时候,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>

"""

Django基础三之路由、视图、模板-LMLPHP

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有些不同。

Django基础三之路由、视图、模板-LMLPHP

在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. 模板的继承

Django基础三之路由、视图、模板-LMLPHP

示例:

主页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 %}

Django基础三之路由、视图、模板-LMLPHP

Django基础三之路由、视图、模板-LMLPHP

子模板不但能修改被标记的位置,还可以使用模板内容:

{% 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 }}
03-16 23:46