所谓请求和响应就是一种对话式或者说应答式的交互过程。请求是值用户使用浏览器通过http协议发送给服务段的数据,响应是值服务端接收到请求后做响应的处理回复给浏览器的数据。常用的方法就是GET,POST,HEAD,DELETE,TRACE等。
1.请求和响应概述
Django中的请求实际就是视图函数view中的第一个参数,就是固定为request,其为HttpRquest对象,Django接收到http请求后,会根据请求数据创建HttpRequest对象,该对象涵盖了很多包括属性,其涵盖了请求的相关信息。这里介绍几个常用的属性:
- path_info:URL字符串
- method:请求方法,比如‘GET’和‘POST’方法等
- GET:QueryDict查询字典的对象,包含get请求方法的所有数据
- POST:QueryDict查询字典的对象,包含post请求方法的所有数据
- FILES:类似字典的对象,包含所有的上传文件的信息
这里在urls中新增test_req路由
path('test_req',views.test_request)
在视图函数中新增一个test_request函数
def rest_request(request):
print('path info is:',request.path_info)
print('method is :',request.method)
print('request.GET is :', request.GET)
return HttpResponse("test is ok")
运行工程,并在终端进行查看
Django响应中,经常使用的是HttpResponse函数,其参数调用格式如下
HttpResponse(content=响应体,contenttype=响应体数据类型,status=状态码)
常见的contentType有:
text/html 默认的html
text/plain 纯文本
text/css css 文件
text/javascript js文件
multipart/form-data 文件提取
application/json json传输
application/xml xml文件
2.GET和POST请求
无论是GET还是POST请求,统一都是由视图函数接收,通过判断request.method区分是哪种方法,代码一般为
if request.method == 'GET':
处理get请求的逻辑
elif request.method =='POST':
处理post请求的逻辑
else:
其他处理逻辑
创建一个mysite2的工程,如何创建工程参考 Django之url和视图函数,在mysite2同名文件下的urls.py的urlpatterns新增路由
# http:127.0.0.1:8000/test_get_post
path('test_get_post',views.test_get_post),
在mysite2同名文件下的views.py中定义函数test_get_post
def test_get_post(request):
if request.method == 'GET':
pass
elif request.method == "POST":
pass
else:
pass
return HttpResponse('--test get post is ok-- ')
因此get和post请求的总体框架如上所示,接着详细介绍下
2.1 GET请求
GET请求动作,一般用于向浏览器获取/查询数据,能够产生GET请求的场景有:
- 浏览器地址栏输入url,点击回车
- < a href=“地址?key1=value1&key2=value2”>
- form表单中的method方法为GET
如果是GET请求方式,可以给服务端传递数据,通过使用查询字符串(Query String),由于是明文传递,因此切忌不要传递敏感数据
get请求URL格式
xxx?key1=value1&key2=value2
eg:http://127.0.0.1:8000/page=1&a=12
使用查询字符串传递数据的时候,可以使用如下方法获取请求对象的属性
request.GET['参数名']
request.GET.get('参数名','默认值')
request.GET.getlist('参数名')
在mysite2的同名文件夹下的urls.py的url_pattern中新增
path('test_get_post',views.test_get_post),
再视图函数views.py中定义视图函数test_get_post
def test_get_post(request):
if request.method == 'GET':
print(request.GET)
print(request.GET['a'])
print(request.GET.get('c','there is no c'))
print(request.GET.getlist('a'))
elif request.method == "POST":
pass
else:
pass
return HttpResponse('--test get post is ok-- ')
运行python mange.py runserver
在终端处查看如下结果
<QueryDict: {'a': ['100', '300', '400']}>
400
there is no c
['100', '300', '400']
这里注意到,查询字符串中输入的是?a=100&a=200&a=400,使用request.GET[‘a’]输出的是最后一个a
2.2 POST请求
POST请求,一般用于向服务器提交数据,客户端通过表单等POST请求将数据传递给服务器,其格式如:
<form method='post' action='login'>
姓名:<input type=text name='username>
<input type='submit' value='登录>
</form>
服务端在views.py中,通过request.method进行判断
request.method == "POST":
使用post方法传递数据的时候,可以使用如下方法获取请求对象的属性
request.POST['参数名']
request.POST.get('参数名','默认值')
request.POST.getlist('参数名')
注意这里在settings.py中的MIDDLEWARE中取消csrf验证,否则Post提交会提示csrf 403的报错
MIDDLEWARE = [
...
# 'django.middleware.csrf.CsrfViewMiddleware',
...
在views.py新增变量
POST_FORM = '''
<form method = 'post' action = '/test_get_post'>
用户名:<input type = 'text' name = 'uname'>
<input type='submit' value='提交'>
</form>
'''
def test_get_post(request):
if request.method == 'GET':
print(request.GET)
print(request.GET['a'])
print(request.GET.get('c','there is no c'))
print(request.GET.getlist('a'))
return HttpResponse(POST_FORM)
elif request.method == "POST":
request.POST.get['uname']
return HttpResponse('post is ok')
else:
pass
return HttpResponse('--test get post is ok-- ')
输入http:127.0.0.1:80000?a=40
3.模板层
Django中的是MTV模式,Model-Template-View
模板是可以根据字典数据动态变化的Html页面。模板可以根据视图中传递的字典数据动态的生成相应的Html网页
3.1 模板配置
创建模板文件夹<项目名>/templates,即在mysite项目问价下创建templates普通文件夹。
在setttings.py中TEMPPLATES配置项中,做如下配置:
1.BACKEND:指定模板引擎
2.DIRS:模板的搜索目录
3.APP_DIRS:是否要在应用的templates文件夹搜索模板文件
4.OPPTIONS:有关模板的选项
配置项目中,需要做如下修改:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
#修改DIRS目录
'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',
],
},
},
]
3.2模板加载
之前我们都是使用return HttpResposne加载返回一个字符串对象,实际开发的时候,很少会直接返回一个HttpResponse对象,这里创建了模板并使用模板,那如何进行加载模板,主要有以下两种方式:
方式一:
通过loader获取模板,通过HttpResponse进行相应,在视图函数中一般处理为:
from django.template import loader
#1.通过loader加载模板
t = loader.get_template('模板文件名')
#2.将t装换成HTML字符串
html = t.render(字典数据)
#3.用响应对象将转换的字符串内容返回给浏览器
return HttpResponse(html)
因此这里配置好setttings.py中的TEMPLATES .DIRS后,在url_patterns中新增一个路由
path('test_html',views.test_html),
在文件夹templates下新建一个test_html.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>模板层</title>
</head>
<body>
<p>是模板层输出</p>
</body>
</html>
接着在视图函数中新增一个test_html函数
from django.template import loader
def test_html(reqeust):
t = loader.get_template('test_html.html')
html = t.render()
return HttpResponse(html)
再次刷新界面,展示如下所示
方式二:
使用render()直接加载并响应模板,在视图函数中:
from django.shortcuts import render
return render(request,'模板文件名',字典数据)
from django.template import loader
from django.shortcuts import render
def test_html(reqeust):
# 方案1
# t = loader.get_template('test_html.html')
# html = t.render()
# return HttpResponse(html)
#方案2
return render(reqeust, 'test_html.html')
这里既然render提到了字典数据,那视图层与模板层之间的数据如何交互呢?
1.视图函数中可以Python变量封装到字典中传递到模板
def xxx_view(request):
dic = {
'key1':'value1',
'key2':'value2',
}
return render(request,'xxx.html',dic)
2.模板中,可以使用{{ 变量名 }}的语法,调用视图传递进来的变量,这里的变量名就是字典中的key1,key2…
将视图函数views.py中的方案2修改如下:
#方案2
dic = {
'username':'django',
'age':18,
}
return render(reqeust, 'test_html.html',dic)
接着修改test_html.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>模板层</title>
</head>
<body>
<p>
客户为{{ username }}年龄为{{ age }}的模板层展示
</p>
</body>
</html>
结果展示为:
3.3 模板层变量
在3.2中已经使用了模板的变量,在视图函数view中,使用字典传递参数,那可以给模板传递哪些数据类型呢?
-
str 字符串 int 整形
-
list 列表 tuple 元组
-
dict 字典 func 方法
-
obj 类实例化的对象
对应的在模板中使用变量的语法如下所示: -
{{ 变量名 }}
-
{{ 变量名.index }}
-
{{ 变量名.key }}
-
{{ 对象.方法名 }}
-
{{ 函数名 }}
接着上面的示例,在urls.py中新增一个路由
path('test_html_param',views.test_html_param),
新增一个模板为test_html_param.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h3>int 是 {{ int }}</h3>
<h3>str 是 {{ str }}</h3>
<h3>lst 是 {{ lst }}</h3>
<h3>lst 是 {{ lst.0 }}</h3>
<h3>dict 是 {{ dict.a }}</h3>
<h3>dict['a'] 是 {{ dict }}</h3>
<h3>functions 是 {{ func }}</h3>
<h3>class_obj 是 {{ class_obj.say }}</h3>
</body>
</html>
在视图函数中,新增test_html_view函数
def test_html_param(request):
dic = {}
dic['int'] = 88
dic['str'] = 'django'
dic['lst'] = ['tom','jack','Liky']
dic['dict'] = {'a':9,'b':10}
dic['func'] = say_hi
dic['class_obj'] = Dog()
return render(request,'test_html_param.html',dic)
def say_hi():
return 'hello'
class Dog:
def say(self):
return "Dog"
输出结果如下所示:
3.4 模板标签
将一些服务器端的功能嵌入到模板中,例如流程控制。标签的语法是
{% 标签 %}
...
{% 结束标签 %}
if标签
{% if 条件表达式1 %}
...
{% elif 条件表达式2 %}
...
{% else %}
...
{% endif %}
这里针对标签,举例如下所示:
在urls.py中新增一个路由
path("mycal",views.test_cal)
在templates下新增一个mycal.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>计算器</title>
</head>
<body>
<form action="/mycal" method="post">
<input type="text" name="x" value="{{ x }}">
<select name="op">
<option value="add">+加</option>
<option value="sub">-减</option>
<option value="mul">*乘</option>
<option value="div">+/除</option>
</select>
<input type="text" name="y" value="{{ y }}"> =
<span>{{ result }}</span>
<div><input type="submit" value="开始计算"></div>
</form>
</body>
</html>
视图函数views.py中新增
def test_mycal(request):
if request.method == "GET":
return render(request,"mycal.html")
elif request.method == "POST":
# 处理计算
x = request.POST['x']
y = request.POST['y']
op = request.POST['op']
result = 0
x,y = int(x),int(y)
if op == 'add':
result = x + y
elif op == 'mul':
result = x * y
elif op == 'sub':
result = x - y
elif op == 'dic':
result = x / y
else:
pass
# 还要能展示用户上次选择的和填写数据
return render(request,'mycal.html',locals())
运行中发现一个问题,每次选择的运算符号之后,运算之后,运算符号都会停留在+加号,这里没哟记录
了解到select中有一个属性为selected,如果某个选项设置了selected属性,则会把其作为第一项进行显示。结合这个属性,对mycal.html做如下调整
<form action="/mycal" method="post">
<input type="text" name="x" value="{{ x }}">
<select name="op">
<option value="add" {% if op == 'add' %}selected{% endif %}>+加</option>
<option value="sub" {% if op == 'sub' %}selected{% endif %}>-减</option>
<option value="mul" {% if op == 'sub' %}selected{% endif %}>*乘</option>
<option value="div" {% if op == 'sub' %}selected{% endif %}>/除</option>
</select>
<input type="text" name="y" value="{{ y }}"> =
<span>{{ result }}</span>
<div><input type="submit" value="开始计算"></div>
</form>
另一个常用的标签是for标签
{% for 变量 in 可迭代对象 %}
...循环语句
{% empty %}
...可迭代对象无数据是填充的语句
{% endfor %}
3.5 模板层继承
继承的思想在面向对象编程中常常有体现,在目标层也可以利用这样的思想,从而事项模板层的代码复用。
我们村查看豆瓣电影中的界面,看看所谓模板继承的原理。
点击选电影和,电视剧发现界面布局完全一样,唯一不同的就是中间部分展示的内容有所不同。类似如下风格:
模板继承可以使用父模板的内容,子模板直接继承父模板的全部内容并可以覆盖父模板中的相应的块。语法如下所示:
- 定义父模板中的block标签
- 标识出哪些在子模板中是允许被修改的
- block标签,在父模板中定义,可以在子模板中覆盖
定义子模板可以如下操作
- 继承模板 extends标签,写在模板文件的第一行。如{% extends 'base.html %}
- 子模板 重写父模板中的内容块
{% block block_name %}
子模板块用来覆盖父模板中 block_name块的内容
{% endblock block_name %}
这里举例说明
在路由urls.py中新增如下路由
path('base_index',views.base_view),
path('music_index',views.music_view),
path('sport_index',views.sport_view),
在视图函数views.py中新增
def base_view(request):
return render(request,'base.html')
def music_view(request):
return render(request, 'music.html')
def sport_view(request):
return render(request, 'sport.html')
在templates文件夹下新增base.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
{% block mytitle %}
<title>主页</title>
{% endblock %}
</head>
<body>
<a href="/music_index">音乐频道</a>
<a href="/sport_index">体育频道</a>
</br>
{% block info %}
这是主页
{% endblock %}
</br>
<h3>有问题,请联系123456xxxx</h3>
</body>
</html>
由于前文提到父模板可以继承的部分就是在父模板中使用block标注出来的部分,在子模板中extends之后并重写该部分,新增music.html和sport.html
{% extends 'base.html' %}
{% block mytitle %}
<title>音乐频道</title>
{% endblock %}
{% block info %}
欢迎来到音乐频道
{% endblock %}
运行之后如下所示:
项目整体结构如下所示
4.url反向解析
url在代码中一般出现在如下两个地方:
- 超链接 点击之后,跳转到对应的链接url
- 模板表单中的数据,用post提交至url
- 视图函数中-302跳转 HttpResponseRedirect(‘url’)将用户地址栏中的地址跳转至对应的url
url反向解析,指在视图或者模板中,用Path定义的名称动态查找或计算出相应的路由。path语法为:
path(route,views,name=别名)
path(page,views.page_view,name=page_url)
根据path中的name=关键字传参给url确定个唯一确定的名字,在模板或者视图中,可以通这个名字反向推断出此url信息,因为实际开发中url很长,使用反向解析可以很大程度上降低写错的可能性。
4.1 url反向解析-模板
url反向解析在模板中-通过url标签实现地址的反向解析
{% url '别名' %}
{% url '别名' '参数值1' '参数值2' %}
#url中参数一律使用字符串传参
eg:
{% url 'pagen' '400' %}
{% 'person' age='18' name='django' %}
在urls.py下新增两个路由
# http://127.0.0.1:8000/test/url
path('test/url',views.test_url),
path('test_url_result_display',views.test_url_result_display,name='turs'),
这里的url很长,我们需要做个简短的别名进行处理,设置别名为turs
新增视图函数views.py下的处理函数
def test_url(request):
return render(request,'test_url.html')
def test_url_result_display(request):
return HttpResponse('--test url result is ok--')
templates模板下新增一个模板,test_url.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>测试url</title>
</head>
<br>
<a href="http://127.0.0.1:8000/test_url_result_display">绝对地址</a>
<a href="/test_url_result_display">相对地址</a>
</br>
<a href="{% url 'turs' %}">url反向解析</a>
</body>
</html>
那携带参数的url反向解析如何实现呢?
新增一个path路由
path('test_url_result_display/<int:pageNumber>',views.test_url_result_display,name='turs'),
视图函数变更,新增一个参数用于接收
def test_url_result_display(request,pageNumber):
return HttpResponse('--test url result is ok--')
模板中
<a href="{% url 'turs' '100' %}">url反向解析带参数</a>
结果如下所示:
url反向解析,还有很重要的一个功能,比如上文中定义的path变化了,将
path('test_url_result_display_xxx/<int:pageNumber>',views.test_url_result_display,name='turs'),
发现其他的几个都不能访问了,但是反向解析的版本仍然可以使用,这就是url “动态的”绑定。
4.2 url反向解析-视图
上面介绍了url反向解析在模板中如何使用,现在介绍下url反向解析如何在视图函数中使用。
from django.url import reverse
reverse('别名',args[],kwargs=[])
ex:
print(reverse('pagen',args=[300]))
print(reverse('person',kwargs={'name':'django','age':20}))
继续上面的介绍,给url_pattern做如下修改
path('test/url',views.test_url,name='tu'),
视图函数做如下修改
def test_url_result_display(request,pageNumber):
# return HttpResponse('--test url result is ok--')
# 跳转302
from django.urls import reverse
url = reverse('tu')
return HttpResponse(url)
进入之后,点击跳转成功,这里浏览器是如何得知,跳转的位置,通过Loaction进行定位
上一篇:Django之url和视图函数