Django开发笔记(四)四件套二:视图
这篇开始的学习需要借助postman,针对数据解耦开发。自行下载即可。
django的视图主要有2种,分别是函数视图和类视图.现在刚开始学习django,我们先学习函数视图(FBV),后面再学习类视图[CBV].
视图函数里只有两块内容:
- 请求对象request;
- 响应对象HttpResponse、render、redirect。
请求来了是wsgi.py接收并封装了socket:

一、请求方式
GET POST PUT DELETE等
二、请求对象
request请求体有两种格式:
- urlencoded形如: a=1&b=2&c=3
- json形如: {"a":1,"b":2,"c":3}
发送POST请求时获取数据的方式:
request.body request.POST # python只会把urlencoded这种格式的内容解析到request.POST里,不解析json格式的内容
request.POST拿到的urlencoded格式的请求体,返回的是:<QueryDict: {'user': ['bao'], 'pwd': ['1234']}>
可以看到,这个字典键值对里的值,是个列表,但是当我用user = request.POST.get("user") 取的时候,取出来的是 bao ,并不是一个列表。那是因为Django用get方法去取的时候,取的是最后一个值。
所以如果应对一个键传多个值,要获取全部值,就不能用get方法了,就要用user = request.POST.getlist("XXX") 方法了。
发送GET请求时获取数据的方式:
request.GET
获取请求路径:
request.path # 只有路径 request.get_full_path() #包含get参数
这两者的区别:例如当我们的请求是http://127.0.0.1:8000/users?user=jin&pwd=123 的时候,两者的运行结果:
print(request.path) # /users print(request.get_full_path()) # /users?user=jin&pwd=123
获取请求头数据:
request.META
request.META.get("HTTP_HOST") # 拿出请求头上的某个数据
完整views.py(可以不看,知道上面讲的方法就行):
from django.shortcuts import render,HttpResponse
from django.views.decorators.csrf import csrf_exempt
'''
GET / HTTP/1.1
...
请求体 格式有两种: urlencoded: a=1&b=2&c=3 json: {"a":1,"b":2,"c":3}
'''
@csrf_exempt
def index(request):
# 获取请求方式
print(request.method) #POST GET等
# # POST请求获取请求体数据
print(request.body)
print(request.POST) # python只会把urlencoded这种格式的内容解析到request.POST里,不解析json格式的内容、
user = request.POST.get("user") # 使用
pwd = request.POST.get("pwd")
print(user)
# # GET请求获取请求体数据
print(request.GET) # <QueryDict: {'user': ['jin'], 'pwd': ['123']}>
# 获取请求路径:
print(request.path) # /users
print(request.get_full_path()) # /users?user=jin&pwd=123
# 获取请求头
# print(request.META)
print(request.META.get("HTTP_HOST"))
return HttpResponse("ok") def test(request): return HttpResponse("test successfully!")
这节我遇到了一个错误,我使用postman发送POST请求的时候报403 forbidden错,参考了博客:Django CSRF cookie not set.错误 - 蜗牛findbug - 博客园 (cnblogs.com) ,代码里加上了红色框框部分,解决了这个问题:
from django.views.decorators.csrf import csrf_exempt @csrf_exempt

这个问题是解决跨域请求,不建议使用这个注解绕过机制,我找了好久更好的解决方法也没有找到。
关于跨域问题可以参考:
Django Django中的@csrf_exempt是什么|极客教程 (geek-docs.com)
一招彻底解决Django跨域请求问题_django转发请求-CSDN博客
我觉得应该是我postman没有配置好出现了这个问题,但是postman发送get请求就没有这个问题。暂时我还没有找到postman改哪里可以不这样。
2023.12.20更新:
这个问题还可以有另一种解决方案,就是settings.py中有个中间件,对post请求做了一个拦截,注掉就可以了:

三、响应对象
响应对象主要有三种形式:
- HttpResponse()
- render()
- redirect()
1.HttpResponse
Django服务器接收到客户端发送过来的请求后,会将提交上来的这些数据封装成一个 HttpRequest 对象传给视图函数。那么视图函数在处理完相关的逻辑后,也需要返回一个响应给浏览器。而这个响应,我们必须返回 HttpResponseBase 或者他的子类的对象。而 HttpResponse 则是 HttpResponseBase 用得最多的子类。
常用属性:
- content:返回的内容。
- status:返回的HTTP响应状态码。
- content_type:返回的数据的MIME类型,默认为 text/html 。浏览器会根据这个属性,来显示数据。如果是 text/html ,那么就会解析这个字符串,如果 text/plain ,那么就会显示一个纯文本。
- 设置响应头: response['X-Access-Token'] = 'xxxx' 。
例如:
@csrf_exempt
def index(request):
res= HttpResponse("ok")
res["user"]="bao" # 自定义响应头
return res
效果就是响应头多加了这个user:

2.JsonResponse
用来对象 dump 成 json 字符串,然后返回将 json 字符串封装成 Response 对象返回给浏览器。并且他的 Content-Type 是 application/json 。示例代码如默认情况下 JsonResponse 只能对字典进行 dump ,如果想要对非字典的数据进行 dump ,那么需要给 JsonResponse 传递一个 safe=False 参数。
1)不使用JsonResponse:
@csrf_exempt
def index(request):
stu={"name":"李莲花","age":24}
import json
return HttpResponse(json.dumps(stu,ensure_ascii=False)) # 这样可以识别中文字符
此时的结果是:

2)使用JsonResponse(本例也是一个dump字典的例子,下面会有dump列表的例子,注意看区别):
def index(request):
stu={"name":"李莲花","age":24} return JsonResponse(stu)
结果:

可以看出,它已经将json解析了,有自己的json格式了。因为JsonResponse会对我们有一个content_type的设置,源码如下:

3)使用JsonRespose序列化一个列表:
默认情况下 JsonResponse 只能对字典进行 dump ,如果想要对非字典的数据进行 dump ,那么需要给 JsonResponse 传递一个 safe=False 参数。
不加参数就会报错:

加了参数后的代码:
def index(request):
books=[{"姓名":"钱钟书","age":34},{"姓名":"钱媛媛","age":5}]
return JsonResponse(books,safe=False)

3.render对象
render我们第二篇用过,这个单词本身的意思就是渲染,当时我们用的是:render(request,"xx一个页面")
结合一个给定的模板和一个给定的上下文字典,并返回一个渲染后的 HttpResponse 对象。
参数:
/*
request: 用于生成响应的请求对象。
template_name:要使用的模板的完整名称,可选的参数
context:添加到模板上下文的一个字典,
默认是一个空字典。如果字典中的某个值是可调用的,视图将在渲染模板之前调用它。
*/
render(request, template_name[, context])
实例:
views.py:
def index(request):
remote_addr = request.META.get("REMOTE_ADDR")
return render(request,"users/index.html", {"ip":remote_addr})
index.html:
<body>
<h1>欢迎你,{{ip}} </h1>
</body>
最终效果:

后面会有一章节专门学模板语法,就是专门讲render的。
4.redirect
使用Django框架构建Python Web应用程序时,在某些时候必须将用户从一个URL重定向到另一个URL,通过redirect方法实现重定向。
参数可以是:
- 一个绝对的或相对的URL, 将原封不动的作为重定向的位置.
- 一个url的别名: 可以使用reverse来反向解析url
传递要重定向到的一个具体的网址。APPEND_SLASH的实现就是基于redirect。
传递要重定向到的一个具体的网址:
def my_view(request):
...
return redirect("/some/url/")
当然也可以是一个完整的网址:
def my_view(request):
...
return redirect("http://www.baidu.com")
传递一个视图的名称:
def my_view(request):
...
return redirect(reverse("url的别名"))
重定向的时候发了两个请求:

用重定向做一个登陆页面的案例:
users/url.py:
from django.urls import path,re_path
from users.views import index,login,auth
urlpatterns = [
path("",index),
path("login/",login),
path("auth/",auth)
]
views.py:
from django.shortcuts import render,HttpResponse,redirect
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def index(request):
remote_addr = request.META.get("REMOTE_ADDR")
return render(request,"users/index.html", {"ip":remote_addr})
@csrf_exempt
def login(request):
return render(request,"users/login.html")
@csrf_exempt
def auth(request):
# 获取数据
# print("request.post:",request.POST)
user = request.POST.get("user")
pwd = request.POST.get("pwd")
# 模拟数据校验
if user == "admin" and pwd =="123":
return redirect("/users")
else:
msg = "用户名或密码错误"
return render(request,"users/login.html", {"msg":msg}) # 如果返回的是静态页面,渲染一些新信息的时候可以这么玩
# return redirect("/users/login")
users/login.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="http://127.0.0.1:8000/users/auth/" method="post">
用户名:<input name = user type="text">
密 码:<input name = pwd type="password">
<input type="submit"><span style="color: red">{{ msg }}</span>
</form>
</body>
</html>
为了帮助理解,对于这个案例的图:

那么,其实返回静态页面的时候,是可以不用重定向的,也即就是下图框框里两种写法都行:(这里行是因为这里简单,就是一个静态页面)

但是上面的users/那个是动态页面,要渲染,要取页面什么的,就建议使用重定向了。
这个案例还有一个小细节,就是users/login.html文件里的:

这里绝对路径复杂,可以删掉,只留一个相对路径:(但前面的 / 一定要加,不然就不是相对路径了)

重定向之APPEND_SLASH
浏览器url请求的时候最后不加杠“/”,但点击回车后自动补了"/":


这个小动作是Django做的,不是浏览器做的哦。
这个小动作里发生了两次请求,一次重定向:

可以打开浏览器页面的检查(F12)验证一下这个过程。
这个效果是由Django的配置文件settings.py设置的,默认是true。设置为FALSE就是关闭这个功能:
APPEND_SLASH=False
浙公网安备 33010602011771号