MVC与MTV模型
一、MVC
Web服务器开发领域里著名的MVC模式,所谓MVC就是把Web应用分为模型(M),控制器(C)和视图(V)三层,他们之间以一种插件式的、松耦合的方式连接在一起,模型负责业务对象与数据库的映射(ORM),视图负责与用户的交互(页面),控制器接受用户的输入调用模型和视图完成用户的请求,其示意图如下所示:

二、MTV
Django的MTV模式本质上和MVC是一样的,也是为了各组件间保持松耦合关系,只是定义上有些许不同,Django的MTV分别是:
- M 代表模型(Model): 负责业务对象和数据库的关系映射(ORM)。
- T 代表模板 (Template):负责如何把页面展示给用户(html)。
- V 代表视图(View): 负责业务逻辑,并在适当时候调用Model和Template。
- 除了以上三层之外,还需要一个URL分发器,它的作用是将一个个URL的页面请求分发给不同的View处理,View再调用相应的Model和Template,MTV的响应模式如下所示:

Django的下载与基本命令
- 下载Django:pip3 install django==2.0.1
- 创建一个django project: django-admin.py startproject luffy
- 在mysite目录下创建应用:python manage.py startapp app01
- 启动django项目:python manage.py runserver 8080 我们访问:http://127.0.0.1:8080/时就可以看到:
一、文件介绍
- manage.py ----- Django项目里面的工具,通过它可以调用django shell和数据库等。
- settings.py ---- 包含了项目的默认设置,包括数据库信息,调试标志以及其他一些工作的变量。
- urls.py ----- 负责把URL模式映射到应用程序。
静态文件
|
1
2
3
4
5
|
#在settings.py中:STATIC_URL = ‘/static/‘STATICFILES_DIRS=( os.path.join(BASE_DIR,‘static‘),) |
路由
URL配置(URLconf)就像Django 所支撑网站的目录。它的本质是URL与要为该URL调用的视图函数之间的映射表;你就是以这种方式告诉Django,对于客户端发来的某个URL调用哪一段逻辑代码对应执行。
一、简单的路由配置
- 若要从URL 中捕获一个值,只需要在它周围放置一对圆括号。
- 不需要添加一个前导的反斜杠,因为每个URL 都有。例如,应该是^articles 而不是 ^/articles。
- 每个正则表达式前面的‘r‘ 是可选的但是建议加上。它告诉Python 这个字符串是“原始的” —— 字符串中任何字符都不应该转义
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
# 在urls.py中: from django.urls import path,re_pathfrom app01 import views# 路由配置: 路径--------->视图函数urlpatterns = [ path(‘admin/‘, admin.site.urls), #无名分组:捕获的值作为位置参数传递给视图函数 re_path(r‘^articles/([0-9]{4})/$‘, views.year_archive), #year_archive(request,‘2009‘) re_path(r‘^articles/([0-9]{4})/([0-9]{2})/$‘, views.month_archive),# month_archive(request,‘2009‘,‘12‘) re_path(r‘^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$‘, views.article_detail), # month_archive(request,‘2009‘,‘12‘,‘1‘),# month_archive(request,‘2009‘,‘12‘,‘13‘) #有名分组:捕获的值作为关键字参数传递给视图函数 re_path(r‘^articles/(?P<year>[0-9]{4})/$‘, views.year_archive),# month_archive(request, year=‘2005‘) re_path(r‘^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$‘, views.month_archive), # month_archive(request, year=‘2005‘, month=‘03‘) re_path(r‘^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$‘, views.article_detail), # month_archive(request, year=‘2003‘, month=‘03‘, day=‘03‘)] |
二、分发
|
1
2
3
4
5
6
7
8
9
10
11
|
# 在urls.py中:from django.urls import path, re_path, includeurlpatterns = [ path(‘admin/‘, admin.site.urls), # 分发 re_path(r"^app01/", include("app01.urls")), re_path(r"^app02/", include("app02.urls")), re_path(r"^", include("app01.urls")),] |
三、反向解析
获得URL 的最终形式,,对于不同层级,Django 提供不同的工具用于URL 反查:
- 在模板中:使用url 模板标签。
- 在Python 代码中:使用from django.urls import reverse
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
# 在urls.py中:from django.urls import path,re_path,includefrom app01 import views urlpatterns = [ path(‘admin/‘, admin.site.urls), # 反向解析 path(‘login.html/‘,views.login,name="Log"), re_path(r‘^articles/([0-9]{4})/$‘, views.year_archive, name=‘news-year-archive‘), ] # 在模板中:注意参数"""<form action="{% url ‘Log‘ %}" method="post"></form> <a href="{% url ‘news-year-archive‘ 2012 %}">2012 Archive</a> <ul>{% for yearvar in year_list %}<li><a href="{% url ‘news-year-archive‘ yearvar %}">{{ yearvar }} Archive</a></li>{% endfor %}</ul><!--多个参数--><a href="{% url "n4" i1 i2 %}">编辑</a>""" #在views.py中:from django.urls import reversefrom django.shortcuts import render,HttpResponse,redirect def redirect_to_year(request): # 无参 url=reverse(‘Log‘) print(url) #有参 year = 2006 url99=reverse(‘news-year-archive‘, args=(year,)) print(url99) return HttpResponse(‘OK‘) |
四、名称空间
命名空间(英语:Namespace)是表示标识符的可见范围。一个标识符可在多个命名空间中定义,它在不同命名空间中的含义是互不相干的。我们在开发项目时,会经常使用name属性反解出URL,当不小心在不同的app的urls中定义相同的name时,可能会导致URL反解错误,为了避免这种事情发生,引入了命名空间。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
# 在urls.py中:from django.urls import path, re_path, includeurlpatterns = [ path(‘admin/‘, admin.site.urls), # 分发 re_path(r"^app01/", include("app01.urls")), re_path(r"^", include("app01.urls")), # 两个应用中,URL 模式名字一样时: re_path(r"^app01/", include(("app01.urls", "app01"))), re_path(r"^app02/", include(("app02.urls", "app02"))),]#app01中的urls.pyfrom django.urls import path,re_pathfrom app01 import viewsurlpatterns = [ re_path("index/",views.index,name="index")]#app02中的urls.pyfrom django.urls import path,re_pathfrom app02 import viewsurlpatterns = [ re_path("index/",views.index,name="index")]#app01中的views.pyfrom django.shortcuts import render,HttpResponsefrom django.urls import reversedef index(reqeust): return HttpResponse(reverse("app01:index"))#app02中的views.pyfrom django.shortcuts import render,HttpResponsefrom django.urls import reversedef index(reqeust): return HttpResponse(reverse("app02:index")) |
五、django2.0版的path
1、基本实例
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
# 在urls.py中:from django.urls import path, re_pathfrom app01 import viewsurlpatterns = [ re_path(‘articles/(?P<year>[0-9]{4})/‘, views.year_archive), re_path(‘article/(?P<article_id>[a-zA-Z0-9]+)/detail/‘, views.detail_view), re_path(‘articles/(?P<article_id>[a-zA-Z0-9]+)/edit/‘, views.edit_view), re_path(‘articles/(?P<article_id>[a-zA-Z0-9]+)/delete/‘, views.delete_view),]"""1.函数 year_archive 中year参数是字符串类型的2.三个路由中article_id都是同样的正则表达式,但是你需要写三遍,当之后article_id规则改变后,需要同时修改三处代码,在Django2.0中,可以使用 path 解决以上的两个问题。"""urlpatterns = [ path(‘articles/2003/‘, views.special_case_2003), path(‘articles/<int:year>/‘, views.year_archive), path(‘articles/<int:year>/<int:month>/‘, views.month_archive), path(‘articles/<int:year>/<int:month>/<slug>/‘, views.article_detail),] |
2、基本规则
- 使用尖括号(<>)从url中捕获值。
- 捕获值中可以包含一个转化器类型(converter type),比如使用 捕获一个整数变量。若果没有转化器,将匹配任何字符串,当然也包括了 / 字符。
- 无需添加前导斜杠。

3、path转化器
Django默认支持以下5个转化器:
- str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
- int,匹配正整数,包含0。
- slug,匹配字母、数字以及横杠、下划线组成的字符串。
- uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
- path,匹配任何非空字符串,包含了路径分隔符
4、注册自定义转化器
对于一些复杂或者复用的需要,可以定义自己的转化器。转化器是一个类或接口,它的要求有三点:
- regex 类属性,字符串类型
- to_python(self, value) 方法,value是由类属性 regex 所匹配到的字符串,返回具体的Python变量值,以供Django传递到对应的视图函数中。
- to_url(self, value) 方法,和 to_python 相反,value是一个具体的Python变量值,返回其字符串,通常用于url反向引用。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
# urls.py:from django.urls import register_converter, pathfrom app01 import yearconvert,views#使用register_converter 将其注册到URL配置中:register_converter(yearconvert.FourDigitYearConverter, ‘yyyy‘)urlpatterns = [ path(‘articles/<yyyy:year>/‘, views.year_archive),]#app01.yearconvert.py:class FourDigitYearConverter: regex = ‘[0-9]{4}‘ #规则 def to_python(self, value): return int(value) # 在这转换了类型 def to_url(self, value): return ‘%04d‘ % value#app01.views.py:from django.shortcuts import render,HttpResponse,redirectdef year_archive(request,year): print(year,type(year)) return HttpResponse(‘ok‘) |
视图
一个视图函数,简称视图,是一个简单的Python 函数,它接受Web请求并且返回Web响应。响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片. . . 是任何东西都可以。无论视图本身包含什么逻辑,都要返回响应。代码写在哪里也无所谓,只要它在你的Python目录下面。除此之外没有更多的要求了。为了将代码放在某处,约定是将视图放置在项目或应用程序目录中的名为views.py的文件中。
一、HttpRequest对象
django将请求报文中的请求行、首部信息、内容主体封装成 HttpRequest 类中的属性。
1.request属性
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
"""1.HttpRequest.GET 一个类似于字典的对象,包含 HTTP GET 的所有参数。详情请参考 QueryDict 对象。2.HttpRequest.POST 一个类似于字典的对象,如果请求中包含表单数据,则将这些数据封装成 QueryDict 对象。 POST 请求可以带有空的 POST 字典 —— 如果通过 HTTP POST 方法发送一个表单,但是表单中没有任何的数据,QueryDict 对象依然会被创建。 因此,不应该使用 if request.POST 来检查使用的是否是POST 方法;应该使用 if request.method == "POST" 另外:如果使用 POST 上传文件的话,文件信息将包含在 FILES 属性中。 注意:键值对的值是多个的时候,比如checkbox类型的input标签,select标签,需要用: request.POST.getlist("hobby")3.HttpRequest.body 一个字符串,代表请求报文的主体。在处理非 HTTP 形式的报文时非常有用,例如:二进制图片、XML,Json等。 但是,如果要处理表单数据的时候,推荐还是使用 HttpRequest.POST 。4.HttpRequest.path 一个字符串,表示请求的路径组件(不含域名)。 例如:"/music/bands/the_beatles/"5.HttpRequest.method 一个字符串,表示请求使用的HTTP 方法。必须使用大写。 例如:"GET"、"POST"6.HttpRequest.encoding 一个字符串,表示提交的数据的编码方式(如果为 None 则表示使用 DEFAULT_CHARSET 的设置,默认为 ‘utf-8‘)。 这个属性是可写的,你可以修改它来修改访问表单数据使用的编码。 接下来对属性的任何访问(例如从 GET 或 POST 中读取数据)将使用新的 encoding 值。 如果你知道表单数据的编码不是 DEFAULT_CHARSET ,则使用它。7.HttpRequest.META 一个标准的Python 字典,包含所有的HTTP 首部。具体的头部信息取决于客户端和服务器,下面是一些示例: CONTENT_LENGTH —— 请求的正文的长度(是一个字符串)。 CONTENT_TYPE —— 请求的正文的MIME 类型。 HTTP_ACCEPT —— 响应可接收的Content-Type。 HTTP_ACCEPT_ENCODING —— 响应可接收的编码。 HTTP_ACCEPT_LANGUAGE —— 响应可接收的语言。 HTTP_HOST —— 客服端发送的HTTP Host 头部。 HTTP_REFERER —— Referring 页面。 HTTP_USER_AGENT —— 客户端的user-agent 字符串。 QUERY_STRING —— 单个字符串形式的查询字符串(未解析过的形式)。 REMOTE_ADDR —— 客户端的IP 地址。 REMOTE_HOST —— 客户端的主机名。 REMOTE_USER —— 服务器认证后的用户。 REQUEST_METHOD —— 一个字符串,例如"GET" 或"POST"。 SERVER_NAME —— 服务器的主机名。 SERVER_PORT —— 服务器的端口(是一个字符串)。 从上面可以看到,除 CONTENT_LENGTH 和 CONTENT_TYPE 之外,请求中的任何 HTTP 首部转换为 META 的键时, 都会将所有字母大写并将连接符替换为下划线最后加上 HTTP_ 前缀。 所以,一个叫做 X-Bender 的头部将转换成 META 中的 HTTP_X_BENDER 键。8.HttpRequest.FILES 一个类似于字典的对象,包含所有的上传文件信息。 FILES 中的每个键为<input type="file" name="" /> 中的name,值则为对应的数据。 注意,FILES 只有在请求的方法为POST 且提交的<form> 带有enctype="multipart/form-data" 的情况下才会 包含数据。否则,FILES 将为一个空的类似于字典的对象。9.HttpRequest.COOKIES 一个标准的Python 字典,包含所有的cookie。键和值都为字符串。10.HttpRequest.session 一个既可读又可写的类似于字典的对象,表示当前的会话。只有当Django 启用会话的支持时才可用。 完整的细节参见会话的文档。11.HttpRequest.user(用户认证组件下使用) 一个 AUTH_USER_MODEL 类型的对象,表示当前登录的用户。 如果用户当前没有登录,user 将设置为 django.contrib.auth.models.AnonymousUser 的一个实例。你可以通过 is_authenticated() 区分它们。 例如: if request.user.is_authenticated(): # Do something for logged-in users. else: # Do something for anonymous users. user 只有当Django 启用 AuthenticationMiddleware 中间件时才可用。 ------------------------------------------------------------------------------------- 匿名用户 class models.AnonymousUser django.contrib.auth.models.AnonymousUser 类实现了django.contrib.auth.models.User 接口,但具有下面几个不同点: id 永远为None。 username 永远为空字符串。 get_username() 永远返回空字符串。 is_staff 和 is_superuser 永远为False。 is_active 永远为 False。 groups 和 user_permissions 永远为空。 is_anonymous() 返回True 而不是False。 is_authenticated() 返回False 而不是True。 set_password()、check_password()、save() 和delete() 引发 NotImplementedError。 New in Django 1.8: 新增 AnonymousUser.get_username() 以更好地模拟 django.contrib.auth.models.User。""" |
2.request常用方法
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
"""1.HttpRequest.get_full_path() 返回 path,如果可以将加上查询字符串。 例如:"/music/bands/the_beatles/?print=true"2.HttpRequest.is_ajax() 如果请求是通过XMLHttpRequest 发起的,则返回True,方法是检查 HTTP_X_REQUESTED_WITH 相应的首部是否是字符串‘XMLHttpRequest‘。 大部分现代的 JavaScript 库都会发送这个头部。如果你编写自己的 XMLHttpRequest 调用(在浏览器端),你必须手工设置这个值来让 is_ajax() 可以工作。 如果一个响应需要根据请求是否是通过AJAX 发起的,并且你正在使用某种形式的缓存例如Django 的 cache middleware, 你应该使用 vary_on_headers(‘HTTP_X_REQUESTED_WITH‘) 装饰你的视图以让响应能够正确地缓存。""" |
二、HttpResponse对象
响应对象主要有三种形式:
- HttpResponse():HttpResponse()括号内直接跟一个具体的字符串作为响应体,比较直接很简单,所以这里主要介绍后面两种形式。
- render():render方法就是将一个模板页面中的模板语法进行渲染,最终渲染成一个html页面作为响应体。
- redirect():重定向一个URL
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
"""render(request, template_name[, context])结合一个给定的模板和一个给定的上下文字典,并返回一个渲染后的 HttpResponse 对象。参数: request: 用于生成响应的请求对象。 template_name:要使用的模板的完整名称,可选的参数 context:添加到模板上下文的一个字典。默认是一个空字典。如果字典中的某个值是可调用的,视图将在渲染模板之前调用它。 render方法就是将一个模板页面中的模板语法进行渲染,最终渲染成一个html页面作为响应体。"""return render(request,"index.html",{"timer":ctime}) # index.html 模板文件# return render(request, "index.html", locals()) ## HttpResponsereturn HttpResponse(reverse("app01:index"))return HttpResponse("<h1>OK</h1>")#redirectreturn redirect("/index/")return redirect(‘http://baidu.com/‘) |
模板
一、模板语法之变量
在 Django 模板中遍历复杂数据结构的关键是句点字符, 语法: {{var_name}}
|
1
2
3
4
5
6
|
<h4>{{s}}</h4><h4>列表:{{ l.0 }}</h4><h4>列表:{{ l.2 }}</h4><h4>字典:{{ dic.name }}</h4><h4>日期:{{ date.year }}</h4><h4>类对象列表:{{ person_list.0.name }}</h4> |
二、模板之过滤器
语法:{{obj|filter__name:param}}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
"""default:如果一个变量是false或者为空,使用给定的默认值。否则,使用变量的值。例如:{{ value|default:"nothing" }}length:返回值的长度。它对字符串和列表都起作用。例如:{{ value|length }} 如果 value 是 [‘a‘, ‘b‘, ‘c‘, ‘d‘],那么输出是 4。filesizeformat:将值格式化为一个 “人类可读的” 文件尺寸 (例如 ‘13 KB‘, ‘4.1 MB‘, ‘102 bytes‘, 等等)。例如:{{ value|filesizeformat }} 如果 value 是 123456789,输出将会是 117.7 MB。 date:如果 value=datetime.datetime.now(){{ value|date:"Y-m-d" }} slice:如果 value="hello world"{{ value|slice:"2:-1" }}truncatechars:如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾。参数:要截断的字符数{{ value|truncatechars:9 }}safe:Django的模板中会对HTML标签和JS等语法标签进行自动转义,这样是为了安全。如果不希望HTML元素被转义,可以这样:value="<a href="">点击</a>"{{ value|safe}}""" |
这里简单介绍一些常用的模板的过滤器,更多详见
三、模板之标签
标签看起来像是这样的: {% tag %}。标签比变量更加复杂:一些在输出中创建文本,一些通过循环或逻辑来控制流程,一些加载其后的变量将使用到的额外信息到模版中。一些标签需要开始和结束标签 (例如{% tag %} ...标签 内容 ... {% endtag %})。
1、for标签
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
"""遍历每一个元素:{% for person in person_list %} <p>{{ person.name }}</p>{% endfor %}可以利用{% for obj in list reversed %}反向完成循环。遍历一个字典:{% for key,val in dic.items %} <p>{{ forloop.counter }} {{ key }}:{{ val }}</p>{% endfor %}注:循环序号可以通过{{ forloop }}显示 forloop.counter The current iteration of the loop (1-indexed)forloop.counter0 The current iteration of the loop (0-indexed)forloop.revcounter The number of iterations from the end of the loop (1-indexed)forloop.revcounter0 The number of iterations from the end of the loop (0-indexed)forloop.first True if this is the first time through the loopforloop.last True if this is the last time through the loop""" |
2、for ... empty
|
1
2
3
4
5
6
|
<!--for 标签带有一个可选的{% empty %} 从句,以便在给出的组是空的或者没有被找到时,可以有所操作。-->{% for person in person_list %} <p>{{ forloop.counter0 }} {{ person.name }} , {{ person.age }}</p>{% empty %} <p>列表为空</p>{% endfor %} |
3、if 标签
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<!--注意:filter可以用在if等语句后,simple_tag不可以-->{% if i|multi_fliter:10 > 100 %} <p>100</p>{% else %} <p>{{ i }}</p>{% endif %}<!--多分支-->{% if num > 100 or num < 0 %} <p>无效</p>{% elif num > 80 and num < 100 %} <p>优秀</p>{% else %} <p>凑活吧</p>{% endif %} |
4、with
|
1
2
3
4
5
|
<!--使用一个简单地名字缓存一个复杂的变量-->{% with person_list.1.name as n %} {{ n }} {{ n }}{% endwith %} |
5、csrf_token
|
1
2
3
4
5
|
<form action="" method="post"> {% csrf_token %} <!--这个标签用于跨站请求伪造保护--> <input type="text" name="user"> <input type="submit"></form> |
四、自定义标签和过滤器
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
"""1、在settings中的INSTALLED_APPS配置当前app,不然django无法找到自定义的simple_tag.2、在app中创建templatetags包(包名只能是templatetags)3、创建任意 .py 文件,如:my_tags.py4、在使用自定义simple_tag和filter的html文件中导入之前创建的 my_tags.py5、使用simple_tag和filter(如何调用)注意:filter可以用在if等语句后,simple_tag不可以"""#settings.pyINSTALLED_APPS = [ ‘django.contrib.admin‘, ‘django.contrib.auth‘, ‘django.contrib.contenttypes‘, ‘django.contrib.sessions‘, ‘django.contrib.messages‘, ‘django.contrib.staticfiles‘, "app01", #配置当前app]# app01.templatetags.my_tags.pyfrom django import templatefrom django.utils.safestring import mark_saferegister = template.Library() # register的名字是固定的,不可改变@register.filter #自定义过滤器最多2个参数def multi_fliter(v1, v2): return v1 * v2@register.simple_tag #自定义标签,没有参数个数限制def multi_tag(v1, v2, v3): return v1 * v2 * v3@register.simple_tagdef my_input(id, arg): result = "<input type=‘text‘ id=‘%s‘ class=‘%s‘ />" % (id, arg,) return mark_safe(result)#模板中:"""{% load my_tags %} <!--注意:这块更改过要重启项目--># num = 8<p>{{ num|multi_fliter:20 }}</p><p>{% multi_tag 7 9 6 %}</p><!--注意:filter可以用在if等语句后,simple_tag不可以-->{% if num|multi_fliter:10 > 100 %} <p>100</p>{% else %} <p>{{ num }}</p>{% endif %}""" |
五、模板继承 (extend)
- 不能在一个模版中定义多个相同名字的 block 标签。
- 为了更好的可读性,你也可以给你的 {% endblock %} 标签一个 名字 。例如:{% block content %}...{% endblock content %}
- 子模版不必定义全部父模版中的blocks
- {% extends ‘base.html‘ %}
1、制作模板
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
<!--模版 base.html--><!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> {% block title %} <title>base</title> {% endblock %} {% block css %} {% endblock %}</head><body><div class="header"></div><div class="container"> <div class="row"> <div class="col-md-3"> {% include ‘left1.html‘ %} <!--引入某个区域--> {% include ‘left2.html‘ %} <!--引入某个区域--> </div> <div class="col-md-9"> {% block con %} <h4>content</h4> {% endblock content%} <!--更好的可读性--> </div> </div></div>{% block js %}{% endblock %}</body></html> |
2、继承模板
|
1
2
3
4
5
6
7
8
9
10
11
12
|
{% extends ‘base.html‘ %} <!--它必须是模版中的第一个标签。-->{% block title %}<title>orders</title>{% endblock %}{% block con %}{{ block.super }} <!--获取模板中con的内容--><h4>订单</h4>{% endblock con%}<!--order.html--> |