一.请求与响应
1.请求
回想一下,利用HTTP协议向服务器传参有几种途径?
提取URL的特定部分,如/weather/beijing/2018,可以在服务器端的路由中用正则表达式截取;
查询字符串(query string),形如key1=value1&key2=value2;
请求体(body)中发送的数据,比如表单数据、json、xml;
在http报文的头(header)中。
1 URL路径参数
在定义路由URL时,可以使用正则表达式提取参数的方法从URL中获取请求参数,Django会将提取的参数直接传递到视图的传入参数中。
未命名参数按定义顺序传递, 如
url(r'^weather/([a-z]+)/(\d{4})/$', views.weather),
def weather(request, city, year):
print('city=%s' % city)
print('year=%s' % year)
return HttpResponse('OK')
命名参数按名字传递,如
url(r'^weather/(?P<city>[a-z]+)/(?P<year>\d{4})/$', views.weather),
def weather(request, year, city):
print('city=%s' % city)
print('year=%s' % year)
return HttpResponse('OK')
2 Django中的QueryDict对象
定义在django.http.QueryDict
HttpRequest对象的属性GET、POST都是QueryDict类型的对象
与python字典不同,QueryDict类型的对象用来处理同一个键带有多个值的情况
方法get():根据键获取值
如果一个键同时拥有多个值将获取最后一个值
如果键不存在则返回None值,可以设置默认值进行后续处理
dict.get('键',默认值)
可简写为
dict['键']
方法getlist():根据键获取值,值以列表返回,可以获取指定键的所有值
如果键不存在则返回空列表[],可以设置默认值进行后续处理
dict.getlist('键',默认值)
- 查询字符串Query String
获取请求路径中的查询字符串参数(形如?k1=v1&k2=v2),可以通过request.GET属性获取,返回QueryDict对象。
# /qs/?a=1&b=2&a=3 def qs(request): a = request.GET.get('a') b = request.GET.get('b') alist = request.GET.getlist('a') print(a) # 3 print(b) # 2 print(alist) # ['1', '3'] return HttpResponse('OK')
重要:查询字符串不区分请求方式,即假使客户端进行POST方式的请求,依然可以通过request.GET获取请求中的查询字符串数据。
4 请求体
请求体数据格式不固定,可以是表单类型字符串,可以是JSON字符串,可以是XML字符串,应区别对待。
可以发送请求体数据的请求方式有POST、PUT、PATCH、DELETE。
Django默认开启了CSRF防护,会对上述请求方式进行CSRF防护验证,在测试时可以关闭CSRF防护机制,方法为在settings.py文件中注释掉CSRF中间件,如:
注释CSRF中间件
4.1 表单类型 Form Data
前端发送的表单类型的请求体数据,可以通过request.POST属性获取,返回QueryDict对象。
def get_body(request):
a = request.POST.get('a')
b = request.POST.get('b')
alist = request.POST.getlist('a')
print(a)
print(b)
print(alist)
return HttpResponse('OK')
重要:request.POST只能用来获取POST方式的请求体表单数据。
4.2 非表单类型 Non-Form Data
非表单类型的请求体数据,Django无法自动解析,可以通过request.body属性获取最原始的请求体数据,自己按照请求体格式(JSON、XML等)进行解析。request.body返回bytes类型。
例如要获取请求体中的如下JSON数据
{"a": 1, "b": 2}
可以进行如下方法操作:
import json
def get_body_json(request):
json_str = request.body
json_str = json_str.decode() # python3.6 无需执行此步
req_data = json.loads(json_str)
print(req_data['a'])
print(req_data['b'])
return HttpResponse('OK')
5 请求头
可以通过request.META属性获取请求头headers中的数据,request.META为字典类型。
常见的请求头如:
CONTENT_LENGTH – The length of the request body (as a string).
CONTENT_TYPE – The MIME type of the request body.
HTTP_ACCEPT – Acceptable content types for the response.
HTTP_ACCEPT_ENCODING – Acceptable encodings for the response.
HTTP_ACCEPT_LANGUAGE – Acceptable languages for the response.
HTTP_HOST – The HTTP Host header sent by the client.
HTTP_REFERER – The referring page, if any.
HTTP_USER_AGENT – The client’s user-agent string.
QUERY_STRING – The query string, as a single (unparsed) string.
REMOTE_ADDR – The IP address of the client.
REMOTE_HOST – The hostname of the client.
REMOTE_USER – The user authenticated by the Web server, if any.
REQUEST_METHOD – A string such as “GET” or “POST”.
SERVER_NAME – The hostname of the server.
SERVER_PORT – The port of the server (as a string).
具体使用如:
def get_headers(request):
print(request.META['CONTENT_TYPE'])
return HttpResponse('OK')
6 其他常用HttpRequest对象属性
method:一个字符串,表示请求使用的HTTP方法,常用值包括:‘GET’、‘POST’。
user:请求的用户对象。
path:一个字符串,表示请求的页面的完整路径,不包含域名和参数部分。
encoding:一个字符串,表示提交的数据的编码方式。
如果为None则表示使用浏览器的默认设置,一般为utf-8。
这个属性是可写的,可以通过修改它来修改访问表单数据使用的编码,接下来对属性的任何访问将使用新的encoding值。
FILES:一个类似于字典的对象,包含所有的上传文件。
2.响应
视图在接收请求并处理后,必须返回HttpResponse对象或子对象。HttpRequest对象由Django创建,HttpResponse对象由开发人员创建。
1 HttpResponse
可以使用django.http.HttpResponse来构造响应对象。
HttpResponse(content=响应体, content_type=响应体数据类型, status=状态码)
也可通过HttpResponse对象属性来设置响应体、状态码:
content:表示返回的内容。
status_code:返回的HTTP响应状态码。
响应头可以直接将HttpResponse对象当做字典进行响应头键值对的设置:
response = HttpResponse()
response['Itcast'] = 'Python' # 自定义响应头Itcast, 值为Python
示例:
from django.http import HttpResponse
def demo_view(request):
return HttpResponse('itcast python', status=400)
或者
response = HttpResponse('itcast python')
response.status_code = 400
response['Itcast'] = 'Python'
return response
2 HttpResponse子类
Django提供了一系列HttpResponse的子类,可以快速设置状态码
HttpResponseRedirect 301
HttpResponsePermanentRedirect 302
HttpResponseNotModified 304
HttpResponseBadRequest 400
HttpResponseNotFound 404
HttpResponseForbidden 403
HttpResponseNotAllowed 405
HttpResponseGone 410
HttpResponseServerError 500
3 JsonResponse
若要返回json数据,可以使用JsonResponse来构造响应对象,作用:
帮助我们将数据转换为json字符串
设置响应头Content-Type为 application/json
from django.http import JsonResponse
def demo_view(request):
return JsonResponse({'city': 'beijing', 'subject': 'python'})
4 redirect重定向
from django.shortcuts import redirect
def demo_view(request):
return redirect('/index.html')
3.Cookie
Cookie,有时也用其复数形式Cookies,指某些网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密)。Cookie最早是网景公司的前雇员Lou Montulli在1993年3月的发明。Cookie是由服务器端生成,发送给User-Agent(一般是浏览器),浏览器会将Cookie的key/value保存到某个目录下的文本文件内,下次请求同一网站时就发送该Cookie给服务器(前提是浏览器设置为启用cookie)。Cookie名称和值可以由服务器端开发自己定义,这样服务器可以知道该用户是否是合法用户以及是否需要重新登录等。服务器可以利用Cookies包含信息的任意性来筛选并经常性维护这些信息,以判断在HTTP传输中的状态。Cookies最典型记住用户名。
Cookie是存储在浏览器中的一段纯文本信息,建议不要存储敏感信息如密码,因为电脑上的浏览器可能被其它人使用。
Cookie的特点
Cookie以键值对的格式进行信息的存储。
Cookie基于域名安全,不同域名的Cookie是不能互相访问的,如访问itcast.cn时向浏览器中写了Cookie信息,使用同一浏览器访问baidu.com时,无法访问到itcast.cn写的Cookie信息。
当浏览器请求某网站时,会将浏览器存储的跟网站相关的所有Cookie信息提交给网站服务器。
1 设置Cookie
可以通过HttpResponse对象中的set_cookie方法来设置cookie。
HttpResponse.set_cookie(cookie名, value=cookie值, max_age=cookie有效期)
max_age 单位为秒,默认为None。如果是临时cookie,可将max_age设置为None。
示例:
def demo_view(request):
response = HttpResponse('ok')
response.set_cookie('itcast1', 'python1') # 临时cookie
response.set_cookie('itcast2', 'python2', max_age=3600) # 有效期一小时
return response
2 读取Cookie
可以通过HttpRequest对象的COOKIES属性来读取本次请求携带的cookie值。request.COOKIES为字典类型。
def demo_view(request):
cookie1 = request.COOKIES.get('itcast1')
print(cookie1)
return HttpResponse('OK')
4.Session
1 启用Session
Django项目默认启用Session。
可以在settings.py文件中查看,如图所示
session中间件
如需禁用session,将上图中的session中间件注释掉即可。
2 存储方式
在settings.py文件中,可以设置session数据的存储方式,可以保存在数据库、本地缓存等。
2.1 数据库
存储在数据库中,如下设置可以写,也可以不写,这是默认存储方式。
SESSION_ENGINE='django.contrib.sessions.backends.db'
如果存储在数据库中,需要在项INSTALLED_APPS中安装Session应用。
session_app
数据库中的表如图所示
session数据库
表结构如下
session表结构
由表结构可知,操作Session包括三个数据:键,值,过期时间。
2.2 本地缓存
存储在本机内存中,如果丢失则不能找回,比数据库的方式读写更快。
SESSION_ENGINE='django.contrib.sessions.backends.cache'
2.3 混合存储
优先从本机内存中存取,如果没有则从数据库中存取。
SESSION_ENGINE='django.contrib.sessions.backends.cached_db'
2.4 Redis
在redis中保存session,需要引入第三方扩展,我们可以使用django-redis来解决。
1) 安装扩展
pip install django-redis
2)配置
在settings.py文件中做如下设置
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "default"
注意
如果redis的ip地址不是本地回环127.0.0.1,而是其他地址,访问Django时,可能出现Redis连接错误,如下:
redis连接错误
解决方法:
修改redis的配置文件,添加特定ip地址。
打开redis的配置文件
sudo vim /etc/redis/redis.conf
在如下配置项进行修改(如要添加10.211.55.5地址)
修改redis配置文件
重新启动redis服务
sudo service redis-server restart
3 Session操作
通过HttpRequest对象的session属性进行会话的读写操作。
1) 以键值对的格式写session。
request.session['键']=值
2)根据键读取值。
request.session.get('键',默认值)
3)清除所有session,在存储中删除值部分。
request.session.clear()
4)清除session数据,在存储中删除session的整条数据。
request.session.flush()
5)删除session中的指定键及值,在存储中只删除某个键及对应的值。
del request.session['键']
6)设置session的有效期
request.session.set_expiry(value)
如果value是一个整数,session将在value秒没有活动后过期。
如果value为0,那么用户session的Cookie将在用户的浏览器关闭时过期。
如果value为None,那么session有效期将采用系统默认值,默认为两周,可以通过在settings.py中设置SESSION_COOKIE_AGE来设置全局默认值。
二.类视图
1 类视图引入
以函数的方式定义的视图称为函数视图,函数视图便于理解。但是遇到一个视图对应的路径提供了多种不同HTTP请求方式的支持时,便需要在一个函数中编写不同的业务逻辑,代码可读性与复用性都不佳。
def register(request):
"""处理注册"""
# 获取请求方法,判断是GET/POST请求
if request.method == 'GET':
# 处理GET请求,返回注册页面
return render(request, 'register.html')
else:
# 处理POST请求,实现注册逻辑
return HttpResponse('这里实现注册逻辑')
在Django中也可以使用类来定义一个视图,称为类视图。
使用类视图可以将视图对应的不同请求方式以类中的不同方法来区别定义。如下所示
from django.views.generic import View
class RegisterView(View):
"""类视图:处理注册"""
def get(self, request):
"""处理GET请求,返回注册页面"""
return render(request, 'register.html')
def post(self, request):
"""处理POST请求,实现注册逻辑"""
return HttpResponse('这里实现注册逻辑')
类视图的好处:
代码可读性好
类视图相对于函数视图有更高的复用性, 如果其他地方需要用到某个类视图的某个特定逻辑,直接继承该类视图即可
2 类视图使用
定义类视图需要继承自Django提供的父类View,可使用from django.views.generic import View或者from django.views.generic.base import View 导入,定义方式如上所示。
配置路由时,使用类视图的as_view()方法来添加。
urlpatterns = [
# 视图函数:注册
# url(r'^register/$', views.register, name='register'),
# 类视图:注册
url(r'^register/$', views.RegisterView.as_view(), name='register'),
]
3 类视图原理
@classonlymethod
def as_view(cls, **initkwargs):
"""
Main entry point for a request-response process.
"""
...省略代码...
def view(request, *args, **kwargs):
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
# 调用dispatch方法,按照不同请求方式调用不同请求方法
return self.dispatch(request, *args, **kwargs)
...省略代码...
# 返回真正的函数视图
return view
def dispatch(self, request, *args, **kwargs):
# Try to dispatch to the right method; if a method doesn't exist,
# defer to the error handler. Also defer to the error handler if the
# request method isn't on the approved list.
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
4 类视图使用装饰器
为类视图添加装饰器,可以使用两种方法。
为了理解方便,我们先来定义一个为函数视图准备的装饰器(在设计装饰器时基本都以函数视图作为考虑的被装饰对象),及一个要被装饰的类视图。
def my_decorator(func):
def wrapper(request, *args, **kwargs):
print('自定义装饰器被调用了')
print('请求路径%s' % request.path)
return func(request, *args, **kwargs)
return wrapper
class DemoView(View):
def get(self, request):
print('get方法')
return HttpResponse('ok')
def post(self, request):
print('post方法')
return HttpResponse('ok')
4.1 在URL配置中装饰
urlpatterns = [
url(r'^demo/$', my_decorate(DemoView.as_view()))
]
此种方式最简单,但因装饰行为被放置到了url配置中,单看视图的时候无法知道此视图还被添加了装饰器,不利于代码的完整性,不建议使用。
此种方式会为类视图中的所有请求方法都加上装饰器行为(因为是在视图入口处,分发请求方式前)。
4.2 在类视图中装饰
在类视图中使用为函数视图准备的装饰器时,不能直接添加装饰器,需要使用method_decorator将其转换为适用于类视图方法的装饰器。
method_decorator装饰器使用name参数指明被装饰的方法
# 为全部请求方法添加装饰器
@method_decorator(my_decorator, name='dispatch')
class DemoView(View):
def get(self, request):
print('get方法')
return HttpResponse('ok')
def post(self, request):
print('post方法')
return HttpResponse('ok')
# 为特定请求方法添加装饰器
@method_decorator(my_decorator, name='get')
class DemoView(View):
def get(self, request):
print('get方法')
return HttpResponse('ok')
def post(self, request):
print('post方法')
return HttpResponse('ok')
如果需要为类视图的多个方法添加装饰器,但又不是所有的方法(为所有方法添加装饰器参考上面例子),可以直接在需要添加装饰器的方法上使用method_decorator,如下所示
from django.utils.decorators import method_decorator
# 为特定请求方法添加装饰器
class DemoView(View):
@method_decorator(my_decorator) # 为get方法添加了装饰器
def get(self, request):
print('get方法')
return HttpResponse('ok')
@method_decorator(my_decorator) # 为post方法添加了装饰器
def post(self, request):
print('post方法')
return HttpResponse('ok')
def put(self, request): # 没有为put方法添加装饰器
print('put方法')
return HttpResponse('ok')
5 类视图Mixin扩展类
使用面向对象多继承的特性,可以通过定义父类(作为扩展类),在父类中定义想要向类视图补充的方法,类视图继承这些扩展父类,便可实现代码复用。
定义的扩展父类名称通常以Mixin结尾。
举例如下:
class ListModelMixin(object):
"""
list扩展类
"""
def list(self, request, *args, **kwargs):
...
class CreateModelMixin(object):
"""
create扩展类
"""
def create(self, request, *args, **kwargs):
...
class BooksView(CreateModelMixin, ListModelMixin, View):
"""
同时继承两个扩展类,复用list和create方法
"""
def get(self, request):
self.list(request)
...
def post(self, request):
self.create(request)
...
class SaveOrderView(CreateModelMixin, View):
"""
继承CreateModelMixin扩展类,复用create方法
"""
def post(self, request):
self.create(request)
...
三.中间件
Django中的中间件是一个轻量级、底层的插件系统,可以介入Django的请求和响应处理过程,修改Django的输入或输出。中间件的设计为开发者提供了一种无侵入式的开发方式,增强了Django框架的健壮性。
我们可以使用中间件,在Django处理视图的不同阶段对输入或输出进行干预。
1 中间件的定义方法
定义一个中间件工厂函数,然后返回一个可以别调用的中间件。
中间件工厂函数需要接收一个可以调用的get_response对象。
返回的中间件也是一个可以被调用的对象,并且像视图一样需要接收一个request对象参数,返回一个response对象。
def simple_middleware(get_response):
# 此处编写的代码仅在Django第一次配置和初始化的时候执行一次。
def middleware(request):
# 此处编写的代码会在每个请求处理视图前被调用。
response = get_response(request)
# 此处编写的代码会在每个请求处理视图之后被调用。
return response
return middleware
例如,在users应用中新建一个middleware.py文件,
def my_middleware(get_response):
print('init 被调用')
def middleware(request):
print('before request 被调用')
response = get_response(request)
print('after response 被调用')
return response
return middleware
定义好中间件后,需要在settings.py 文件中添加注册中间件
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'users.middleware.my_middleware', # 添加中间件
]
定义一个视图进行测试
def demo_view(request):
print('view 视图被调用')
return HttpResponse('OK')
执行结果
中间件测试
注意:Django运行在调试模式下,中间件init部分有可能被调用两次。
2 执行流程
中间件执行流程
3 多个中间件的执行顺序
在请求视图被处理前,中间件由上至下依次执行
在请求视图被处理后,中间件由下至上依次执行
中间件顺序
示例:
定义两个中间件
def my_middleware(get_response):
print('init 被调用')
def middleware(request):
print('before request 被调用')
response = get_response(request)
print('after response 被调用')
return response
return middleware
def my_middleware2(get_response):
print('init2 被调用')
def middleware(request):
print('before request 2 被调用')
response = get_response(request)
print('after response 2 被调用')
return response
return middleware
注册添加两个中间件
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
# 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'users.middleware.my_middleware', # 添加
'users.middleware.my_middleware2', # 添加
]
执行结果
init2 被调用
init 被调用
before request 被调用
before request 2 被调用
view 视图被调用
after response 2 被调用
after response 被调用
四.模板使用
1 配置
在工程中创建模板目录templates。
在settings.py配置文件中修改TEMPLATES配置项的DIRS值:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')], # 此处修改
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
2 定义模板
在templates目录中新建一个模板文件,如index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>{{ city }}</h1>
</body>
</html>
3 模板渲染
调用模板分为两步骤:
找到模板 loader.get_template(模板文件在模板目录中的相对路径) -> 返回模板对象
渲染模板 模板对象.render(context=None, request=None) -> 返回渲染后的html文本字符串 context 为模板变量字典,默认值为None request 为请求对象,默认值为None
例如,定义一个视图
from django.http import HttpResponse
from django.template import loader
def index(request):
# 1.获取模板
template=loader.get_template('index.html')
context={'city': '北京'}
# 2.渲染模板
return HttpResponse(template.render(context))
Django提供了一个函数render可以简写上述代码。
render(request对象, 模板文件路径, 模板数据字典)
from django.shortcuts import render
def index(request):
context={'city': '北京'}
return render(request,'index.html',context)
4 模板语法
4.1 模板变量
变量名必须由字母、数字、下划线(不能以下划线开头)和点组成。
语法如下:
{{变量}}
模板变量可以使python的内建类型,也可以是对象。
def index(request):
context = {
'city': '北京',
'adict': {
'name': '西游记',
'author': '吴承恩'
},
'alist': [1, 2, 3, 4, 5]
}
return render(request, 'index.html', context)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>{{ city }}</h1>
<h1>{{ adict }}</h1>
<h1>{{ adict.name }}</h1> 注意字典的取值方法
<h1>{{ alist }}</h1>
<h1>{{ alist.0 }}</h1> 注意列表的取值方法
</body>
</html>
4.2 模板语句
1)for循环:
{% for item in 列表 %}
循环逻辑
{{forloop.counter}}表示当前是第几次循环,从1开始
{%empty%} 列表为空或不存在时执行此逻辑
{% endfor %}
2)if条件:
{% if ... %}
逻辑1
{% elif ... %}
逻辑2
{% else %}
逻辑3
{% endif %}
比较运算符如下:
==
!=
<
>
<=
>=
布尔运算符如下:
and
or
not
注意:运算符左右两侧不能紧挨变量或常量,必须有空格。
{% if a == 1 %} # 正确
{% if a==1 %} # 错误
4.3 过滤器
语法如下:
使用管道符号|来应用过滤器,用于进行计算、转换操作,可以使用在变量、标签中。
如果过滤器需要参数,则使用冒号:传递参数。
变量|过滤器:参数
列举几个如下:
safe,禁用转义,告诉模板这个变量是安全的,可以解释执行
length,长度,返回字符串包含字符的个数,或列表、元组、字典的元素个数。
default,默认值,如果变量不存在时则返回默认值。
data|default:'默认值'
date,日期,用于对日期类型的值进行字符串格式化,常用的格式化字符如下:
Y表示年,格式为4位,y表示两位的年。
m表示月,格式为01,02,12等。
d表示日, 格式为01,02等。
j表示日,格式为1,2等。
H表示时,24进制,h表示12进制的时。
i表示分,为0-59。
s表示秒,为0-59。
value|date:"Y年m月j日 H时i分s秒"
4.4 注释
1)单行注释语法如下:
{#...#}
2)多行注释使用comment标签,语法如下:
{% comment %}
...
{% endcomment %}
4.5 模板继承
模板继承和类的继承含义是一样的,主要是为了提高代码重用,减轻开发人员的工作量。
父模板
如果发现在多个模板中某些内容相同,那就应该把这段内容定义到父模板中。
标签block:用于在父模板中预留区域,留给子模板填充差异性的内容,名字不能相同。 为了更好的可读性,建议给endblock标签写上名字,这个名字与对应的block名字相同。父模板中也可以使用上下文中传递过来的数据。
{% block 名称 %}
预留区域,可以编写默认内容,也可以没有默认内容
{% endblock 名称 %}
子模板
标签extends:继承,写在子模板文件的第一行。
{% extends "父模板路径"%}
子模版不用填充父模版中的所有预留区域,如果子模版没有填充,则使用父模版定义的默认值。
填充父模板中指定名称的预留区域。
{% block 名称 %}
实际填充内容
{{ block.super }}用于获取父模板中block的内容
{% endblock 名称 %}