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'),

解析:

  1. 使用render这种方式,网址不会变化,还是原来的,这样修改过后就只能展示一次列表,没有了能够在列表上编辑和修改的功能了。
  2. 想要网址也变化,就需要在前端form的actioan参数中添加'userlist.html'网址,但是这个网址是POST提交数据的网址,就与修改数据的网址产生了冲突。

综上,所以还是直接写重定向好。

HttpResponse、render和redirect的三者的区别

  1. HttpResponse 用于返回一个 HTTP 响应。可以使用这个函数返回 HTML 页面、XML 数据、JSON 等内容。但是需要手动把数据填充到响应体中。需要注意的是,HttpResponse只是将请求处理后的响应给了浏览器,但是并没有改变浏览器的地址栏。

  2. render 用于将数据填充到模板中并返回渲染后的 HTML 响应。一般用于渲染动态页面。render 函数通常需要传递模板文件名、数据等参数。render不会直接跳转页面,而是返回一个新的URL,并在用户离开原始URL地址后自动替换为新的URL。这意味着用户可以保存页面,并可以通过历史记录进行回退。

  3. 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.'
    )

上传文件

表单上传文件需要满足的条件是:

  1. 请求方式需要是:post
  2. 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()),

分析步骤:

  1. as_view()是类View里面的as_view方法
    第二个参数是as_view方法里面的view的函数内存地址
  2. 当http请求过来的时候,会自定触发view的执行
  3. view()中的返回值self.dispatch()
  4. 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')

对应知识点:

  1. 类来调用方法:

    • 被@classmethod装饰器修饰的方法
    • 被@staticmethod装饰器修饰的方法
  2. 函数名加括号执行优先级最高 项目一启动就会自动执行as_view方法

  3. 对象点方法:
    先从自己类中查找,没有去继承的父类中查找

  4. 对象点属性的查找顺序:
    先从自己对象属性里面去找,如果找到了,直接返回,如果找不到,在去产生这个对象的类中取查找,如果也没有,去继承的父类中查找,还没有,则报错。

  5. getattr():

    反射指的是'通过字符串来操作对象的属性'

    getattr(对象,'对象属性的字符串',默认值)
    

    给了第三个参数,如果属性存在,就输出属性值,属性不存在,输出默认值,不会改变对象字典。

posted @ 2023-04-27 20:37  星空看海  阅读(17)  评论(0编辑  收藏  举报