Django基础之视图层
三板斧
本质
用来处理请求的视图函数都必须返回HttpResponse对象
# 可以查看源码
1.HttpResponse():肯定是返回HttpResponse对象
2.render():是一个函数,它的源码中返回值也是一个HttpResponse对象
return HttpResponse(content, content_type, status)
3.redirect():它的返回值redirect_class继承的类中往上一直寻找,祖先有个类是HttpResponse
思考
为什么删除数据之后要直接重定向,而不可以到展示数据的页面?
redirect('/index/') # index函数的功能是展示展示一个userlist页面
render(request, 'userlist.html'),
解析:
- 使用render这种方式,网址不会变化,还是原来的,这样修改过后就只能展示一次列表,没有了能够在列表上编辑和修改的功能了。
- 想要网址也变化,就需要在前端form的actioan参数中添加'userlist.html'网址,但是这个网址是POST提交数据的网址,就与修改数据的网址产生了冲突。
综上,所以还是直接写重定向好。
HttpResponse、render和redirect的三者的区别
-
HttpResponse 用于返回一个 HTTP 响应。可以使用这个函数返回 HTML 页面、XML 数据、JSON 等内容。但是需要手动把数据填充到响应体中。需要注意的是,HttpResponse只是将请求处理后的响应给了浏览器,但是并没有改变浏览器的地址栏。
-
render 用于将数据填充到模板中并返回渲染后的 HTML 响应。一般用于渲染动态页面。render 函数通常需要传递模板文件名、数据等参数。render不会直接跳转页面,而是返回一个新的URL,并在用户离开原始URL地址后自动替换为新的URL。这意味着用户可以保存页面,并可以通过历史记录进行回退。
-
redirect 用于重定向浏览器到一个新的 URL。一般用于提交表单之后进行重定向或页面跳转。redirect 函数需要传递重定向的 URL 或者一个路由名。需要注意的是,在使用redirect时,浏览器会向新的URL再次发送请求,这就意味着在处理跳转后的请求时,与之前的请求进行的操作是不同的。
JsonResponse
json格式的数据的作用:可以进行跨语言传输
json格式数据举例:
{"username":"kevin"}
# 用双引号
复习之前学习python中和js中序列化
1.python中:
序列化: json.dumps()
反序列化: json.loads()
2.js中:
序列化: JSON.stringify()
反序列化: JSON.parse()
在django中的序列化:
1.正常使用
# 在django中的序列化,要导入JsonResponse
from django.http import JsonResponse
def ab_json(request):
user_dict = {'username': 'tank', 'age': 20}
# 方式一:使用python序列化
# import json
# json_str = json.dumps(user_dict)
# Django中序列化
json_str = JsonResponse(user_dict)
return HttpResponse(json_str)
2.修改字符编码
def ab_json(request):
user_dict = {'username': 'tank哈哈哈', 'age': 20}
# python中序列化修改字符编码
# import json
# json_str = json.dumps(user_dict,ensure_ascii=False)
# Django中序列化修改字符编码
json_str = JsonResponse(user_dict, json_dumps_params={'ensure_ascii': False})
'''就可以显示中文了'''
# 当你遇到一些新的方法的时候,点进去看一看,追一追代码,大概应该能看懂
return HttpResponse(json_str)
可以查看源码,查看底层的逻辑。JsonResponse的底层还是dumps,只是在dumps函数基础之上做了封装。
源码分析:
data = json.dumps(data, cls=encoder, **json_dumps_params)
**json_dumps_params有为空的判断
# 添加参数:json_dumps_params={'ensure_ascii': False}之后的变化
====>
data = json.dumps(data, cls=encoder, **{'ensure_ascii': False})
====>
data = json.dumps(data, cls=encoder, ensure_ascii=False)
3.序列化列表
def ab_json(request):
l = [1, 2, 3, 4]
# python中序列化列表,变成数组形式
# Django中序列化列表
# 为了允许序列化非字典对象,要将安全参数设置为False。
json_str = JsonResponse(l,safe=False)
return HttpResponse(json_str)
源码:
JsonResponse是个类
里面__init__方法,中设置参数safe=True
存在一个判断条件:
# 如果safe是真并且data不是字典类型,就会主动抛出异常。
if safe and not isinstance(data, dict):
raise TypeError(
'In order to allow non-dict objects to be serialized set the '
'safe parameter to False.'
)
上传文件
表单上传文件需要满足的条件是:
- 请求方式需要是:post
- enctype---->form-data
django后端获取文件类型的数据:request.FILES
具体使用:
前端html:
<form action="" method="post" enctype="multipart/form-data">
views.py
def ab_file(request):
# 接收用户提交过来的数据
# request.POST获取普通数据(输入、选择)
print(request.POST) # <QueryDict: {'username': ['SDASD']}>
# request.FILES,获取文件数据(上传)
print(request.FILES) # <MultiValueDict: {'myfile': [<InMemoryUploadedFile: 123.png (image/png)>]}>
# 'myfile':文件对象
# 先得到文件对象
file_obj = request.FILES.get('myfile')
# 读取图片文件
with open(file_obj.name, 'wb') as f:
# 可以一次性读取
# f.write(file_obj)
# 文件比较大的时候,建议这种方式
for line in file_obj: # 文件对象支持循环,就可以一行一行读取
f.write(line)
return render(request, 'ab_file.html')
Django视图层-FBV与CBV
- FBV:基于函数的视图
# function based view
def index(request):return HttpResponse对象
- CBV:基于类的视图
# class based view
class Mylogin(View): def get(reguest): pass
FBV中request对象的几个方法
# GET POST FILES method
# path path_info get_full_path() body
def index(request):
print(request.path) # /index/
print(request.path_info) # /index/
print(request.get_full_path()) # 接收路径的全部内容,连参数也能拿到 /index/?username=kevin
print(request.body) # 浏览器发过的二进制数据
return HttpResponse('ok')
CBV
路由:
url(r'^login/', views.Mylogin.as_view()),
views.py:
# 新建类
from django.http import HttpResponse
from django.views import View
class Mylogin(View): # CBV必须继承View类
# 通过get请求方式会访问到这个方法
def get(self, request):
return HttpResponse('get')
# 通过post请求方式会访问到这个方法
def post(self, request):
return HttpResponse('post')
'''CBV会自动根据请求方法的不同自动匹配对应的方法并执行'''
CBV的源码剖析
看源码首先先找入口,CBV的入口就在路由:
url(r'^login/', views.Mylogin.as_view()),
分析步骤:
- as_view()是类View里面的as_view方法
第二个参数是as_view方法里面的view的函数内存地址 - 当http请求过来的时候,会自定触发view的执行
- view()中的返回值self.dispatch()
- View类里面的dispatch方法
详细分析:
1.从路由入口
url(r'^login/', views.Mylogin.as_view()), # 类名调用方法(1.可能是绑定给类的方法,2.可能是静态方法)
我们在MyLogin类中并没有写as_view()方法, 可能会在父类中。 ---> 看父类View()
2.父类View(),内部有as_view()方法
# class classonlymethod(classmethod)
# 这个装饰器继承了classmethod类,可以把它当成classmethod,但功能更加强大
@classonlymethod
def as_view(cls, **initkwargs):
'''结果as_view()是绑定给类的方法'''
3.as_view()方法详细
'''使用是,as_view()加括号是调用了这个方法'''
@classonlymethod
def as_view(cls, **initkwargs):
# 类来调用方法,会把类名当成第一个参数传给方法的第一个形参
# cls:Mylogin
for循环...,参数验证
# 闭包,定义在函数内部的函数
def view(request, *args, **kwargs):pass
return view # 返回值是函数名
######
views.MyLogin.as_view()-------->View类里面的as_view方法中的view方法的内存地址
url(r'^login/', views.Mylogin.as_view()),
# 类似于url(r'^login/',views.view),本质上是放的还是函数的内存地址
当访问这个地址时,是向服务端发送了个请求,请求来了,会自动触发对应视图函数的执行,即view函数的执行。
4.view()函数详细
def view(request, *args, **kwargs):
self = cls(**initkwargs)
# cls是使用了外部函数中的值,cls:Mylogin
# self = Mylogin(**initkwargs),类名加括号是实例化一个对象
# self:是我们写的类产生的对象
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
# 以上就是传参数
'''在看跟面向对象相关的源码的时候,一定要区分开self是谁'''
# self是我们自己写的类产生的对象
# 对象点方法的查找顺序:先从自己的类中查找,没有去父类中查找
return self.dispatch(request, *args, **kwargs)
'''
view函数执行完毕之后,来到了return self.dispatch(request, *args, **kwargs)
对象调方法,是把对象自己当成第一个参数传入
'''
5.View类中的dispath()方法
def dispatch(self, request, *args, **kwargs):
# 请求方式转成小写
# self:我们自己写的类产生的对象
# self.http_method_names,是对象点属性,对象名称空间中没有,Mylogin类中也没有,去父类View中找。
# View类中有:http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'],View类定义的8中请求方式
if request.method.lower() in self.http_method_names:
# 反射,getattr(self, 'get', 默认值)
# 从对象中找get方法,没有执行第三个参数
# 找到,handler是get函数的内存地址
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else: # 如果不是上面8中请求,方法不允许
handler = self.http_method_not_allowed
'''所以我们定义的类中的方法名不可以随意写,写的方法必须是8中请求方式中的一种'''
return handler(request, *args, **kwargs) # get(request, *args, **kwargs),get加括号调用,把参数传给get函数
'''所以我们定义类,写方法时,需要写request参数'''
我们要熟练口头表述出步骤逻辑。
这是最简单的源码,后面还会学习权限、频率等的源码。这些源码更加复杂。
使用操作
我们写的类中的使用源码的两个简单操作:
1.我写的这个类只允许get和post请求,怎么做?
类的内部定义一个类属性http_method_names,名字不要写错!!!
rom django.views import View
class MyLogin(View):
# 我写的这个类只允许get和post请求
http_method_names = ['get', 'post']
def get(self, request): # 通过get请求方式会访问到这个方法
return HttpResponse('get')
def post(self, request): # 通过post请求方式会访问到这个方法
return HttpResponse('post')
解析:
对象点属性时,是先从产生对象的这个类中查找,找到就直接返回,我们定义了一个属性,就不会再使用源码中定义的属性了。并且只会影响这一个类,对其他的类不会产生影响。
2.View类定义一个dispath()
如果我们自己写的类中也定义了一个dispath()方法,就会先查找到我们自己的dispath()方法,不会去父类中查找dispath了。
from django.views import View
class AddPublish(View):
def dispatch(self, request, *args, **kwargs):
print(request)
print(args)
print(kwargs)
# 可以写类似装饰器的东西,在前后加代码
obj=super().dispatch(request, *args, **kwargs)
return obj
def get(self,request):
return HttpResponse('get')
def post(self,request):
print(request.POST)
return HttpResponse('post')
对应知识点:
-
类来调用方法:
- 被@classmethod装饰器修饰的方法
- 被@staticmethod装饰器修饰的方法
-
函数名加括号执行优先级最高 项目一启动就会自动执行as_view方法
-
对象点方法:
先从自己类中查找,没有去继承的父类中查找 -
对象点属性的查找顺序:
先从自己对象属性里面去找,如果找到了,直接返回,如果找不到,在去产生这个对象的类中取查找,如果也没有,去继承的父类中查找,还没有,则报错。 -
getattr():
反射指的是'通过字符串来操作对象的属性'
getattr(对象,'对象属性的字符串',默认值)
给了第三个参数,如果属性存在,就输出属性值,属性不存在,输出默认值,不会改变对象字典。