一、Django的路由层php
URL配置(URLconf)就像Django所支撑网站的目录。它的本质是URL与要为该URL调用的视图函数之间的映射表;咱们就是以这种方式告诉Django,对于客户端发来的某个URL该调用哪一段逻辑代码对应执行。css
(1)、简单的路由配置html
from django.contrib import admin from django.urls import path, re_path from blog import views urlpatterns = [ path('admin/', admin.site.urls), re_path(r'^$', views.index), # 含有正则匹配根路径 path('shopping', views.shopping), # 不含正则使用path re_path(r'^active/(\d{4})/$', views.active), # 正则也可使用分组 ,匹配任意四位数字 如 active/1234/ ]
解释:urlpatterns列表中是访问路径和视图函数的映射关系。python
注意:程序员
^articles
而不是 ^/articles
。(2)有名分组ajax
上面的示例使用简单的、没有命名的正则表达式组(经过圆括号)来捕获URL 中的值并以位置 参数传递给视图。在更高级的用法中,可使用命名的正则表达式组来捕获URL 中的值并以关键字 参数传递给视图。正则表达式
在python 正则表达式中, 命名正则表达式组的语法是(?P<name>正则表达式), 其中 name 是组的名字 , 正则表达式 是要匹配的内容。数据库
from django.contrib import admin from django.urls import path, re_path from blog import views urlpatterns = [ re_path(r'^articles/2003/$', views.special_case_2003), re_path(r'^articles/(?P<year>\d{4})/$', views.year_archive), re_path(r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/$', views.month_archive), re_path(r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/$', views.article_detail), path('admin/', admin.site.urls), 】
这个实现与前面的示例彻底相同,只有一个细微的差异:捕获的值做为关键字参数而不是位置参数传递给视图函数。django
在实际应用中,这意味你的URLconf 会更加明晰且不容易产生参数顺序问题的错误 —— 你能够在你的视图函数定义中从新安排参数的顺序。固然,这些好处是以简洁为代价。浏览器
(3)path转换器
from django.contrib import admin from django.urls import path, re_path from blog import views urlpatterns = [ path('articles/2018/', views.special_case_2018), path('articles/<int:year>/', views.year_archive), path('articles/<int:year>/<int:month>/', views.month_archive), path('articles/<int:year>/<int:month>/<slug:slug>/', views.article_detail), ]
默认状况下,Django内置下面的路径转换器:
(4)、分发
咱们发现,Django生成的项目中只有一个全局的urls.py文件,这就意味着咱们须要把全部应用的访问路径与视图函数的映射关系都放到这一个urls.py文件中,但应用较多时会发现这样作不太好,因而咱们但愿每一个应用各自的映射关系可以放到本身的应用目录中。这是咱们就要用到分发的功能,也就是在每一个app里,各自建立一个urls.py
路由模块,而后从根路由出发,将app所属的url请求,所有转发到相应的urls.py
模块中。
项目的urls.py文件:
from django.contrib import admin from django.urls import path, re_path, include urlpatterns = [ re_path(r'^admin/', admin.site.urls), re_path(r'^blog/', include('blog.urls')), ]
blog的urls.py文件:
from django.urls import re_path from blog import views urlpatterns = [ re_path(r'^articles/(\d{4})/$', views.article_year), re_path(r'^articles/2003/$', views.special_year), re_path(r'^articles/(\d{4})/(\d{2})/$', views.article_month), re_path(r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/(?P<day>\d{2})/$', views.article_day), ]
(5)、反向解析
在使用Django 项目时,一个常见的需求是得到URL的最终形式,以用于嵌入到生成的内容中(视图中和显示给用户的URL等)或者用于处理服务器端的导航(重定向等)。人们强烈但愿不要硬编码这些URL(费力、不可扩展且容易产生错误)或者设计一种与URLconf 绝不相关的专门的URL 生成机制,由于这样容易致使必定程度上产生过时的URL(简而言之,代码中的URL写死就容易出现问题)。
在须要URL 的地方,对于不一样层级,Django 提供不一样的工具用于URL 反查:
1)在模板中的url:使用url 模板标签{% url "别名" 动态变量名 %}。
2)在Python 代码中:使用from django.urls import reverse函数。
urls.py文件
from django.urls import re_path from blog import views urlpatterns = [ re_path(r'^articles/([0-9]{4})/$', views.year_archive, name='year-archive'), # 给这个路径起了一个别名year-archive, 注意url匹配中有正则,也就是说url中有动态参数,因此url模板标签有要有动态变量接收 ]
在模板中(此示例是url含动态变量的状况,不含动态变量则不写动态变量名便可):
<a href="{% url 'year-archive' 2012 %}">2012 Archive</a> 若上面别名为year-archive的url是:http://www.baidu.com/articles/2008/ 则这个a标签连接为:http://www.baidu.com/articles/2008/2012 <ul> {% for yearvar in year_list %} <li><a href="{% url 'year-archive' yearvar %}">{{ yearvar }} Archive</a></li> {% endfor %} </ul>
在python代码(视图函数中,此示例url中含动态变量,没有动态变量则不写args=()便可):
from django.urls import reverse from django.http import HttpResponseRedirect def redirect_to_year(request): year = 2006 return HttpResponseRedirect( reverse('year-archive', args=(year,))) # 同redirect("/path/"), args=(year,)接收url中动态变量
注意:当命名你的URL 模式时,请确保使用的名称不会与其它应用中名称冲突。若是你的URL 模式叫作comment,而另一个应用中也有一个一样的名称,当你在模板中使用这个名称的时候不能保证将插入哪一个URL。在URL 名称中加上一个前缀,好比应用的名称,将减小冲突的可能。咱们建议使用myapp-comment 而不是comment
(6)、命名空间
命名空间(英语:Namespace)是表示标识符的可见范围。一个标识符可在多个命名空间中定义,它在不一样命名空间中的含义是互不相干的。这样,在一个新的命名空间中可定义任何标识符,它们不会与任何已有的标识符发生冲突,由于已有的定义都处于其它命名空间中。
上面反向解析中,因为name没有做用域,Django在反解URL时,会在项目全局顺序搜索,当查找到第一个name指定URL时,当即返回。咱们在开发项目时,会常用name属性反解出URL,当不当心在不一样的app的urls中定义相同的name时,可能会致使URL反解错误,为了不这种事情发生,引入了命名空间。以下示例:
项目下urls.py文件:
urlpatterns = [
url(r'^admin/', admin.site.urls), url(r'^app01/', include("app01.urls", namespace="app01")), url(r'^app02/', include("app02.urls", namespace="app02")), ]
app01下的urls.py:
urlpatterns = [
url(r'^index/', index, name="index"), ]
app02下的urls.py:
urlpatterns = [
url(r'^index/', index, name="index"), ]
app01下的views.py:
from django.core.urlresolvers import reverse def index(request): return HttpResponse(reverse("app01:index"))
app02下的views.py:
from django.core.urlresolvers import reverse def index(request): return HttpResponse(reverse("app02:index"))
二、Django的视图层
(1)、视图函数
一个视图函数,简称视图,是一个简单的Python函数,它接受Web请求而且返回Web响应。响应能够是一张网页的HTML内容,一个重定向,一个404错误,一个XML文档,或者一张图片. . . ,是任何东西均可以。不管视图自己包含什么逻辑,都要返回响应。代码写在哪里也无所谓,只要它在你的Python目录下面。除此以外没有更多的要求了——能够说“没有什么神奇的地方”。为了将代码放在某处,约定是将视图放置在项目或应用程序目录中的名为views.py的文件中。
下面是一个返回包含当前日期和时间的HTML文档的视图:
from django.shortcuts import render, HttpResponse import datetime def current_datetime(request): now = datetime.datetime.now() html = "<html><body>It is now %s.</body></html>" % now # 这里html页面代码有删减,不该该这么写,为了便于理解这样表示 return HttpResponse(html)
分析上面的代码:
首先,咱们从django.shortcuts模块导入了HttpResponse类,以及python的datetime库。
接着,咱们定义了current_datetime函数,它就是视图函数。每一个视图函数都使用HttpRequest对象做为第一个参数,而且一般称之为request。
注意,视图函数的名称并不重要;不须要用一个统一的命名方式来命名,以便让Django识别它。咱们将其命名为current_datetime,是由于这个名称可以精确地反映出它的功能。
这个视图会返回一个HttpResponse对象,其中包含生成的响应。每一个视图函数都负责返回一个HttpResponse对象。
总结:视图层,熟练掌握两个对象便可:请求对象(request)和响应对象(HttpResponse)。
(2)、HttpRequest对象
a、request属性
django将请求报文中的请求首行、头部信息、内容主体封装成 HttpRequest 类中的属性。 除了特殊说明的以外,其余均为只读的。
①、HttpRequest.GET 一个相似于字典的对象,包含 HTTP GET的全部参数。详情请参考 QueryDict 对象。 ②、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")。 ③、HttpRequest.body 一个字符串,表明请求报文的主体。在处理非 HTTP 形式的报文时很是有用,例如:二进制图片、XML,Json等。 可是,若是要处理表单数据的时候,推荐仍是使用 HttpRequest.POST。 ④、HttpRequest.path 一个字符串,表示请求的路径(不含域名和参数),例如:"/music/bands/the_beatles/"。 ⑤、HttpRequest.method 一个字符串,表示请求使用的HTTP 方法,比较时必须使用大写,例如:"GET"、"POST"。 ⑥、HttpRequest.encoding 一个字符串,表示提交的数据的编码方式(若是为 None 则表示使用DEFAULT_CHARSET 的设置,默认为 'utf-8')。 这个属性是可写的,你能够经过修改它来修改访问表单数据使用的编码。 接下来对属性的任何访问(例如从GET或POST中读取数据)将使用新的 encoding 值。若是你知道表单数据的编码不是DEFAULT_CHARSET,则使用它。 ⑦、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 键。 ⑧、HttpRequest.FILES 一个相似于字典的对象,包含全部的上传文件信息。 FILES中的每一个键为<input type="file" name="" /> 中的name,值则为对应的数据。 注意,FILES 只有在请求的方法为POST 且提交的<form> 带有enctype="multipart/form-data" 的状况下才会包含数据。不然,FILES 将为一个空的相似于字典的对象。 ⑨、HttpRequest.COOKIES 一个标准的Python 字典,包含全部的cookie。键和值都为字符串。 ⑩、HttpRequest.session 一个既可读又可写的相似于字典的对象,表示当前的会话。只有当Django 启用会话的支持时才可用。完整的细节参见会话的文档。 ⑪、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 中间件时才可用。
b、request经常使用方法
①、HttpRequest.get_full_path() 返回 path,若是能够将加上查询字符串,例如:"/music/bands/the_beatles/?print=true"。 ②、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') 装饰你的视图以让响应可以正确地缓存。
(3)、HttpResponse对象
响应对象主要有三种形式(其实都是从HttpResponse中抽离出来的,即本质是最后必定响应一个HttpResponse的实例对象):
1)HttpResponse()
2)render()
3)redirect()
①、HttpResponse()
HttpResponse("字符串")括号内直接跟一个具体的字符串做为响应体,比较直接很简单。
②、render()
语法:render(request, template_name[, context])
结合一个给定的模板和一个给定的上下文字典,并返回一个渲染后的 HttpResponse 对象。
参数说明:
request: 用于生成响应的请求对象;
template_name:要使用的模板的完整名称;
context:添加到模板上下文的一个字典。默认是一个空字典。若是字典中的某个值是可调用的,视图将在渲染模板以前调用它(这里也能够写成locals(),表明将全部变量传递给页面);
render方法就是将一个模板页面中的模板语法进行渲染,最终渲染成一个html页面做为响应体(render有两个功能:一个是读取文件字符串,另外一个是渲染变量)。
③、redirect()
传递要重定向的一个硬编码的URL,例如:
def my_view(request): ... return redirect('/some/url/')
也能够是一个完整的URL,例如:
def my_view(request): ... return redirect('http://example.com/')
注意:重定向时,当接收到浏览器的第一次请求后,若写了重定向,则至关于告诉浏览器再立刻向指定的地址再发送一次,即一共发送了两次请求。
补充:
1)301和302的区别:
301和302状态码都表示重定向,就是说浏览器在拿到服务器返回的这个状态码后会自动跳转到一个新的URL地址,这个地址能够从响应的Location首部中获取(用户看到的效果就是他输入的地址A瞬间变成了另外一个地址B)— 这是它们的共同点;
他们的不一样在于:301表示旧地址A的资源已经被永久地移除了(这个资源不可访问了),搜索引擎在抓取新内容的同时也将旧的网址交换为重定向以后的网址;
302表示旧地址A的资源还在(仍然能够访问),这个重定向只是临时地从旧地址A跳转到地址B,搜索引擎会抓取新的内容而保存旧的网址。SEO(搜索引擎优化)302好于301。
2)重定向的缘由:
(1)网站调整(如改变网页目录结构);
(2)网页被移到一个新地址;
(3)网页扩展名改变(如应用须要把.php改为.Html或.shtml);
这种状况下,若是不作重定向,则用户收藏夹或搜索引擎数据库中旧地址只能让访问客户获得一个404页面错误信息,访问流量白白丧失;再者某些注册了多个域名的网站,也须要经过重定向让访问这些域名的用户自动跳转到主站点等。
3)用redirect能够解释APPEND_SLASH的用法!
Django的seetings.py配置文件中默认没有 APPEND_SLASH这个参数,但 Django 默认这个参数为 APPEND_SLASH = True。做用就是自动在url结尾加'/'。
默认地,任何不匹配或尾部没有斜杠(/)的申请URL,将被重定向至尾部包含斜杠的相同字眼的URL。当seetings.py设置为 APPEND_SLASH = False 时,访问 http://example.com/hello 返回 404。
三、Django的模板层
你可能已经注意到上面视图层的例子中返回文本的方式有点特别,也就是说,HTML被直接硬编码在python代码中,以下:
from django.shortcuts import render,HttpResponse import datetime def current_datetime(request): now = datetime.datetime.now() html = "<html><body>It is now %s.</body></html>" % now return HttpResponse(html)
尽管这种方式便于解释视图是如何工做的,但直接将HTML硬编码到你的视图里却并非一个好主意。 让咱们来看一下为何:
1)对页面设计进行的任何改变都必须对 Python 代码进行相应的修改。 站点设计的修改每每比底层 Python 代码的修改要频繁得多,所以若是能够在不进行 Python 代码修改的状况下变动设计,那将会方便得多;
2)Python 代码编写和 HTML 设计是两项不一样的工做,大多数专业的网站开发环境都将他们分配给不一样的人员(甚至不一样部门)来完成。设计者和HTML/CSS的编码人员不该该被要求去编辑Python的代码来完成他们的工做;
3)程序员编写 Python代码和设计人员制做模板两项工做同时进行的效率是最高的,远胜于让一我的等待另外一我的完成对某个既包含 Python又包含 HTML 的文件的编辑工做;
基于这些缘由,将页面的设计和Python的代码分离开会更干净简洁更容易维护。咱们可使用 Django的 模板系统 (Template System)来实现这种模式,这就是下面要具体讨论的问题。
Python的模板:HTML代码 + 模板语法
def current_time(req): # ===============原始的视图函数=============== # import datetime # now = datetime.datetime.now() # html = "<html><body>如今时刻:<h1>%s.</h1></body></html>" %now # ==========django模板修改的视图函数========== # from django.template import Template,Context # now = datetime.datetime.now() # t = Template('<html><body>如今时刻是:<h1>{{current_date}}</h1></body></html>') # t = get_template('current_datetime.html') # c = Context({'current_date':str(now)}) # html = t.render(c) # return HttpResponse(html) # 另外一种写法(推荐) import datetime now = datetime.datetime.now() return render(req, 'current_datetime.html', {'current_date': str(now)[:19]})
学习模板层就要学习Django的模板语法,一共有两种:
1){{ var_name }} --- 渲染变量;
2){% %} --- 渲染标签;
(1)、模板语法之变量 - 深度查询
在 Django 模板中遍历复杂数据结构的关键是句点字符, 语法:{{var_name}},以下示例:
views.py文件中代码:
from django.shortcuts import render def index(request): import datetime s = "hello" # 字符串 lst = [111, 222, 333] # 列表 dic = {"name": "yuan", "age": 18} # 字典 date = datetime.date(1993, 5, 2) # 日期对象 class Person(object): def __init__(self, name): self.name = name person_yuan = Person("yuan") # 自定义类对象 person_egon = Person("egon") person_alex = Person("alex") person_list = [person_yuan, person_egon, person_alex] return render(request, "index.html", {"s": s, "l": lst, "dic": dic, "date": date, "person_list": person_list})
templates中的index.html模板文件代码:
<h4>{{ s }}</h4> <h4>列表:{{ lst.0 }}</h4> <h4>列表:{{ lst.2 }}</h4> <h4>字典:{{ dic.name }}</h4> <h4>日期:{{ date.year }}</h4> <h4>类对象列表:{{ person_list.0.name }}</h4>
注意:句点符也能够用来引用对象的方法(由于引用时不加括号,因此只能引用无参数方法)。
(2)、模板语法之变量 - 过滤器
语法:{{obj|filter_name:param}}
①、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" }} 则输出相似2018-10-22日期格式
⑤、slice
若是 value="hello world",则{{ value|slice:"2:-1" }},输出"llo worl"
⑥、truncatechars
若是字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾。例如{{ value|truncatechars:9 }},将截取6个字符 + ...①
⑦、truncatewords
截取单词,相似truncatechars
⑧、safe
Django的模板中会对HTML标签和JS等语法标签进行自动转义,缘由显而易见,这样是为了安全。可是有的时候咱们可能不但愿这些HTML元素被转义,好比咱们作一个内容管理系统,后台添加的文章中是通过修饰的,这些修饰多是经过一个相似于FCKeditor编辑加注了HTML修饰符的文本,若是自动转义的话显示的就是保护HTML标签的源文件。为了在Django中关闭HTML的自动转义有两种方式,若是是一个单独的变量咱们能够经过过滤器“|safe”的方式告诉Django这段代码是安全的,没必要转义。好比:
value = "<a href="">点击</a>"
{{ value|safe }}
这里简单介绍一些经常使用的模板的过滤器,更多详见
(3)、模板语法之渲染标签
标签渲染看起来像是这样的: {% tag %}。渲染标签语法比渲染变量语法复杂一点:一些在输出中建立文本,一些经过循环或逻辑来控制流程,一些加载其后的变量将使用到的额外信息到模版中。渲染标签须要开始和结束标签(例如{% tag %} ... 内容 ... {% endtag %})。
示例一:for循环遍历一个列表
{% for person in person_list %} <p>{{ person.name }}</p> {% endfor %}
注:能够利用{% for obj in list reversed %}反向实现循环。
示例二:for循环遍历一个字典
{% for key,val in dic.items %} <p>{{ 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 loop forloop.last -- True if this is the last time through the loop
示例三:if控制语句标签
{ % if num > 100 or num < 0 %} <p>无效</p> { % elif num > 80 and num < 100 %} <p>优秀</p> { % else %} <p>凑活吧</p> { % endif %}
说明:{% if %}会对一个变量求值,若是它的值是“True”(存在、不为空、且不是boolean类型的false值),对应的内容块会输出。
示例四:with语句标签
{% with total=business.employees.count %}
{{ total }} employee {{ total|pluralize }} {% endwith %}
说明:使用一个简单地名字缓存一个复杂的变量,当你须要使用一个“昂贵的”方法(好比访问数据库)不少次的时候是很是有用的。
示例五:{{ csrf_token }} -- 这个标签用于跨站请求伪造保护
上篇中咱们提到了当进行post请求时,会发生forbidden错误,那是当浏览器发送post请求时,除了携带用户想提交的数据以外,服务器还要求浏览器携带一个服务器以前发送的标识做为通行证,用来标识该访问是一个正经常使用户,因此直接发送post请求而不携带那个特定标识时,会被服务器拒绝,因此咱们除了能够采起上篇中提到注释掉settings.py文件中的'django.middleware.csrf.CsrfViewMiddleware',还能够在form表单中添加标签{{ csrf_token }},会自动生成一个惟一标识。
示例六:for … empty语句标签
{% for person in person_list %} <p>{{ person.name }}</p> {% empty %} <p>sorry,no person here</p> {% endfor %}
说明:for 标签带有一个可选的{% empty %} 从句,以便在给出的组是空的或者没有被找到时,能够有所操做。
(5)、模板继承
Django模版引擎中最强大也是最复杂的部分就是模版继承了。模版继承可让咱们建立一个基本的“骨架”模版,它包含咱们站点中的所有元素,而且能够定义可以被子模版覆盖的 block 。
经过从下面这个例子开始,能够容易的理解模板继承:
<!DOCTYPE html>
<html lang="en"> <head> <link rel="stylesheet" href="style.css" /> <title>{% block title %}My amazing site{% endblock %}</title> </head> <body> <div id="sidebar"> {% block sidebar %} <ul> <li><a href="/">Home</a></li> <li><a href="/blog/">Blog</a></li> </ul> {% endblock %} </div> <div id="content"> {% block content %} {% endblock %} </div> </body> </html>
这个模版,咱们把它叫做base.html,它定义了一个能够用于两列排版页面的简单HTML骨架。“子模版”的工做是用它们的内容填充这个模板中的block。在这个例子中,block 标签订义了三个能够被子模版内容填充的block。block 告诉模版引擎:子模版可能会覆盖掉模版中的这些位置。
子模版可能看起来是这样的:
{% extends "base.html" %} {% block title %}My amazing blog{% endblock %} {% block content %} {% for entry in blog_entries %} <h2>{{ entry.title }}</h2> <p>{{ entry.body }}</p> {% endfor %} {% endblock %}
extends标签是这里的关键。它告诉模版引擎,这个模版“继承”了另外一个模版。当模版系统处理这个模版时,首先,它将定位父模版(在此例中,就是“base.html”),而后,模版引擎将注意到 base.html 中的三个block标签,并用子模版中的内容来替换这些block。根据 blog_entries 的值,输出可能看起来是这样的:
<!DOCTYPE html>
<html lang="en"> <head> <link rel="stylesheet" href="style.css" /> <title>My amazing blog</title> </head> <body> <div id="sidebar"> <ul> <li><a href="/">Home</a></li> <li><a href="/blog/">Blog</a></li> </ul> </div> <div id="content"> <h2>Entry one</h2> <p>This is my first entry.</p> <h2>Entry two</h2> <p>This is my second entry.</p> </div> </body> </html>
注意,子模版并无定义sidebar block,因此系统使用了父模版中的值。父模版的{% block %}标签中的内容老是被用做备选内容。这种方式使代码获得最大程度的复用,而且使得添加内容到共享的内容区域更加简单,例如,部分范围内的导航。
下面是使用继承的一些提示:
1)若是你在模版中使用{% extends %}标签,它必须是这个模版中的第一个标签。不然,模版继承将没法工做。
2)在base模版中设置越多的{% block %}标签越好。请记住,子模版没必要定义所有父模版中的block,因此,你能够在大多数block中填充合理的默认内容,而后,只定义你须要的那一个。多一点钩子(block)总比少一点好。
3)若是你发现本身在大量的模版中复制内容,那可能意味着你应该把内容移动到父模版中的一个 {% block %} 中。
4)If you need to get the content of the block from the parent template, the {{ block.super }} variable will do the trick. This is useful if you want to add to the contents of a parent block instead of completely overriding it. Data inserted using {{ block.super }} will not be automatically escaped (see the next section), since it was already escaped, if necessary, in the parent template.
5)为了更好的可读性,你也能够给你的 {% endblock %} 标签一个 名字 。例如:
{% block content %}
{% endblock content %}
在大型模版中,这个方法帮你清楚的看到哪个{% block %}标签被关闭了。
6)不能在一个模版中定义多个相同名字的 block 标签。
四、其余
(1)、表单提交的action问题
form表单中的action不写值的话默认提交到当前页面路径,写相对路径的话当前url的域名 : 端口号和action中的相对路径拼接就是表单提交的url。