模板和类视图
1. 在工程中创建模板目录templates。
然后修改settings.py配置文件中修改TEMPLATES配置项的DIRS值, 如下:
2. 定义模板
在templates新建一个index.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{{ city }}
</body>
</html>
3. 类视图的引入
在Django中也可以使用类来定义一个视图,称为类视图。
def register(request):
"""处理注册"""
# 获取请求方法,判断是GET/POST请求
if request.method == 'GET':
# 处理GET请求,返回注册页面
return render(request, 'index.html')
else:
# 处理POST请求,实现注册逻辑
return HttpResponse('这里实现注册逻辑')
使用类视图可以将视图对应的不同请求方式以类中的不同方法来区别定义。如下所示
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('这里实现注册逻辑')
类视图的好处:
- 代码可读性好
- 类视图相对于函数视图有更高的复用性, 如果其他地方需要用到某个类视图的某个特定逻辑,直接继承该类视图即可
4. 类视图的使用
定义类视图需要继承自Django提供的父类View,可使用from django.views.generic import View
或者from django.views.generic.base import View
导入,定义方式如上所示。
from django.views.generic import View
class RegisterView(View):
"""类视图:处理注册"""
def get(self, request):
"""处理GET请求,返回注册页面"""
return render(request, 'register.html')
def post(self, request):
return render(request, 'index.html')
配置路由时,使用类视图的as_view()
方法来添加。
urlpatterns = [
# 视图函数:注册
# url(r'^register/$', views.register, name='register'),
# 类视图:注册
url(r'^register/$', views.RegisterView.as_view(), name='register'),
]
5. 类视图原理
@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)
总结:
URL里面调用as_view,调用dispatch方法,按照不同请求方式调用不同请求方法。最后返回真正的
函数视图View
6. 定义模板,模板渲染
1. 在templates目录中新建一个模板文件,如test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>{{ city }}</h1>
</body>
</html>
2. 调用模板分为两步骤
1) 找到模板 loader.get_template(模板文件在模板目录中的相对路径) -> 返回模板对象
2) 渲染模板 模板对象.render(context=None, request=None) -> 返回渲染后的html文本字符串 context 为模板变量字典,默认值为None request 为请求对象,默认值为None
定义一个视图如下:
from django.http import HttpResponse
from django.template import loader
def test(request):
# 1.获取模板
template = loader.get_template('test.html')
context = {'city': '北京'}
# 2.渲染模板
return HttpResponse(template.render(context))
返回:
上面代码可以简化,djangi提供了render函数
render(request对象, 模板文件路径, 模板数据字典)
from django.http import HttpResponse
from django.template import loader
# def test(request):
# # 1.获取模板
# template = loader.get_template('test.html')
# context = {'city': '北京'}
# # 2.渲染模板
# return HttpResponse(template.render(context))
from django.shortcuts import render
def test(request):
context = {'city': '广州'}
return render(request, 'index.html', context)
7.定义模板,模板渲染
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>
返回:
2. 模板的语言
For循环
{% for item in 列表 %}
循环逻辑
{{forloop.counter}}表示当前是第几次循环,从1开始
{%empty%} 列表为空或不存在时执行此逻辑
{% endfor %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% for foo in alist %}
<h1>{{ foo }}</h1>
{{forloop.counter}}
{% endfor %}
</body>
</html>
IF条件
{% if ... %}
逻辑1
{% elif ... %}
逻辑2
{% else %}
逻辑3
{% endif %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% if city == "北京" %}
<h1>北京</h1>
{% elif city == "北2" %}
<h1>北2</h1>
{% else %}
<h1>else</h1>
{% endif %}
</body>
</html>
比较运算符 和 布尔运算符
==
!=
<
>
<=
>=
and
or
not
{% if a == 1 %} # 正确
{% if a==1 %} # 错误
过滤器
语法如下:
- 使用管道符号|来应用过滤器,用于进行计算、转换操作,可以使用在变量、标签中。
-
如果过滤器需要参数,则使用冒号:传递参数。
变量|过滤器:参数
列举几个如下:
safe,禁用转义,告诉模板这个变量是安全的,可以解释执行
length,长度,返回字符串包含字符的个数,或列表、元组、字典的元素个数。
default,默认值,如果变量不存在时则返回默认值。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{{ now_date|date:"Y-m-j H:i:s" }}<br/>
</body>
</html>
注释
1)单行注释语法如下:
{#...#}
2)多行注释使用comment标签,语法如下:
{% comment %}
...
{% endcomment %}
模板继承
父模板
如果发现在多个模板中某些内容相同,那就应该把这段内容定义到父模板中。
标签block:用于在父模板中预留区域,留给子模板填充差异性的内容,名字不能相同。 为了更好的可读性,建议给endblock标签写上名字,这个名字与对应的block名字相同。父模板中也可以使用上下文中传递过来的数据。
{% block 名称 %}
预留区域,可以编写默认内容,也可以没有默认内容
{% endblock 名称 %}
子模板
标签extends:继承,写在子模板文件的第一行。
{% extends "父模板路径"%}
子模版不用填充父模版中的所有预留区域,如果子模版没有填充,则使用父模版定义的默认值。
填充父模板中指定名称的预留区域。
{% block 名称 %}
实际填充内容
{{ block.super }}用于获取父模板中block的内容
{% endblock 名称 %}
例:
父模板block 名称
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% block index%}
<h1>父模板</h1>
{{ now_date|date:"Y-m-j H:i:s" }}<br/>
{% endblock index %}
</body>
</html>
子模板继承父模板
{% extends "index.html" %} {# 继承父模板 #}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% block index %}
<h1>子模板</h1> {# 子模板实际填充内容 #}
{{ block.super }} {# 父模板,block里的内容 #}
{% endblock index %}
</body>
</html>
返回:
8.中间件
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
2 . 我们需要在setting.py中注册中间件,如下:
3. 定义一个视图
def demo_view(request):
print('view 视图被调用')
return HttpResponse('OK')
返回:
init被调用了两次
原因:
Django运行在调试模式下,中间件init部分有可能被调用两次。
4. 执行流程
5. 多个中间件执行顺序
- 在请求视图被处理前,中间件由上至下依次执行
- 在请求视图被处理后,中间件由下至上依次执行
def my_middleware(get_response):
print('init 被调用')
def middleware(request):
print('before request 被调用......1')
response = get_response(request)
print('after response 被调用.....1')
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', # 添加
]
返回
总结:
中间件在请求视图被处理前,中间件由上至下依次执行
在请求视图被处理后,中间件由下至上依次执行
注意:如果在manage同级下创建了middleware那么每个APP都会调用这个中间件,意思就是全局的。
扩展类方法写中间件:
导入from django.utils.deprecation import MiddlewareMixin, 继承MiddlewareMixin这个类
from django.utils.deprecation import MiddlewareMixin
class my_middleware(MiddlewareMixin):
def process_request(self, request):
print("middle_ware1.process_request......111")
def process_response(self, request, response):
print("middle_ware1.process_response.......2222")
return response
返回
添加两个中间件试试
返回