week11 day5 视图层

养成看源码的习惯。

用大佬的智慧结晶去思考问题,学习大佬使用知识点的方法。

一、三板斧

视图函数必须返回一个HttpResopnse对象。

HttpResponse源码:
在这里插入图片描述
render源码:
在这里插入图片描述

redirect源码:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
如果书写一个视图函数没有return,会抛出异常:

The view app01.views.index didn't return an HttpResponse object. It returned None instead.
'''
HttpResponse
    返回字符串类型
render
    返回html页面 并且在返回给浏览器之前还可以给html文件传值
redirect
    重定向
'''

# render简单内部原理
    from django.template import Template,Context
    Temp = Template('<h1>{{ user }}</h1>')
    con = Context({'user':{'username':'jason','password':123}})
    res = Temp.render(con)
    print(res)
    return HttpResponse(res)

二、JsonResponse对象

Json格式的数据有什么用?
	前后端数据交互需要使用到json作为过渡,实现跨语言的数据传输

前端序列化
	JSON.stringify()		json.dumps()
	JSON.parse()			json.loads()
def ab_json(request):
    from django.shortcuts import HttpResponse
    user_dict = {'username': 'jason', 'password': '123', 'hobby': '他喜欢跑步, 我们称他为跑王'}

    # 方法一. 使用jason将数据格式转换成序列化类型, 再使用HttpResponse将该字符串返回
    import json
    json_str = json.dumps(user_dict, ensure_ascii=False)
    # ensure_ascii=False如果不设置这个参数是会把中文转换的
    return HttpResponse(json_str)

在这里插入图片描述

def ab_json(request):
    from django.shortcuts import HttpResponse
    user_dict = {'username': 'jason', 'password': '123', 'hobby': '他喜欢跑步, 我们称他为跑王'}

    # 一. 使用jason将数据格式转换成序列化类型, 再使用HttpResponse将该字符串返回
    # import json
    # json_str = json.dumps(user_dict, ensure_ascii=False)
    # return HttpResponse(json_str)


    # 二. 使用JsonResponse实现如上功能.
    from django.http import JsonResponse
    '''
    :param json_dumps_params: A dictionary of kwargs passed to json.dumps().  # 一个kwarg字典传递给json.dumps()。
    data = json.dumps(data, cls=encoder, **json_dumps_params)
    '''
    # return JsonResponse(user_dict, json_dumps_params={'ensure_ascii': False})

    li = [111, 222, 333, 444, '他喜欢跑步, 我们称他为跑王']
    '''
    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.'  # 为了允许非dict对象被序列化,设置safe参数为False。
    )
    '''
    return JsonResponse(li, safe=False, json_dumps_params={'ensure_ascii': False})


# 总结
'''
1. JsonResponse默认只能传递字典类型,如果要传递非字典类型需要设置一下safe关键字参数。
    response = JsonResponse([1, 2, 3], safe=False)
    
2. JsonResponse序列化如果想要中文不编码需要指定ensure_ascii为False
    return JsonResponse(user_dict, json_dumps_params={'ensure_ascii': False})
'''

三、form表单上传文件

# form表单上传文件类型的数据:
	1.method必须设置成post
	2.enctype指定属性值为'multipart/form-data'
def ab_file(request):
    if request.method == "POST":
        print(request.POST)    # <QueryDict: {'username': ['xxxxxx'], 'password': ['123456']}>
        print(request.FILES)   # <MultiValueDict: {'file': [<InMemoryUploadedFile: login.html (text/html)>, <InMemoryUploadedFile: index.html (text/html)>, ...]}>

        # 一. 获取MultiValueDict文件对象列表中最后一个文件对象
        file_obj = request.FILES.get('file')
        print(file_obj.name)  # error.html

        # 二. 获取MultiValueDict文件对象列表中多个数据
        '''
        file_obj_list = request.FILES.getlist('file')
        print(file_obj_list[0])  # index.html
        print(file_obj_list[1])  # error.html
        '''

        # 三. 保存文件
        # 写法一:
        '''
        with open(file_obj.name, 'wb') as f:
            for line in file_obj:
                f.write(line)
        '''

        # 写法二: 官方推荐写法
        '''
        def chunks(self, chunk_size=None):
            self.file.seek(0)
            yield self.read()
        '''
        with open(file_obj.name, 'wb') as f:
            for line in file_obj.chunks():
         	       f.write(line)

    return render(request, 'form.html')

四、request对象方法

4.1 常用方法

print(request.method)            # GET    返回大写的字符串格式的请求方式

print(request.GET)               # <QueryDict: {'username': ['123'], 'hobbies': ['play', 'dance']}>  返回QueryDict对象
print(request.POST)

print(request.FILES)             # <MultiValueDict: {'file': [<InMemoryUploadedFile: login.html (text/html)>, <InMemoryUploadedFile: index.html (text/html)>, ...]}>

print(request.path)              # /app02/ab_request/  只能拿到路由
print(request.path_info)         # /app02/ab_request/
print(request.get_full_path())   # /app02/ab_request/?username=jason&password=123  可以拿到路由, 又可以拿到路由?号后面的参数

print(request.body)              # 原生浏览器发送过来的二进制数据

4.2 request.META

print(request.MEAT)              # HTTP请求的其他东西,放在里面,如: 客户端ip地址:REMOTE_ADDR

request.META: {
	'ALLUSERSPROFILE': 'C:\\ProgramData',
	'ASL.LOG': 'Destination=file',
	'COMMONPROGRAMFILES': 'C:\\Program Files\\Common Files',
	'COMMONPROGRAMFILES(X86)': 'C:\\Program Files (x86)\\Common Files',
	'COMMONPROGRAMW6432': 'C:\\Program Files\\Common Files',
	'COMPUTERNAME': 'LAPTOP-EI2EE90I',
	'COMSPEC': 'C:\\WINDOWS\\system32\\cmd.exe',
	'CYGWIN': 'mintty',
	'DJANGO_SETTINGS_MODULE': 'request_data.settings',
	'DRIVERDATA': 'C:\\Windows\\System32\\Drivers\\DriverData',
	'HOMEDRIVE': 'C:',
	'IDEA_INITIAL_DIRECTORY': 'Z:\\pycharm-professional\\PyCharm 2019.3.3\\bin',
	'LOGONSERVER': '\\\\LAPTOP-EI2EE90I',
	'NUMBER_OF_PROCESSORS': '4',
	'OS': 'Windows_NT',
	'PATHEXT': '.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC',
	'PROCESSOR_ARCHITECTURE': 'AMD64',
	'PROCESSOR_IDENTIFIER': 'Intel64 Family 6 Model 158 Stepping 9, GenuineIntel',
	'PROCESSOR_LEVEL': '6',
	'PROCESSOR_REVISION': '9e09',
	'PROGRAMDATA': 'C:\\ProgramData',
	'PROGRAMFILES': 'C:\\Program Files',
	'PROGRAMFILES(X86)': 'C:\\Program Files (x86)',
	'PROGRAMW6432': 'C:\\Program Files',
	'PSMODULEPATH': 'C:\\Program Files\\WindowsPowerShell\\Modules;C:\\WINDOWS\\system32\\WindowsPowerShell\\v1.0\\Modules',
	'PUBLIC': 'C:\\Users\\Public',
	'PYCHARM': 'Z:\\pycharm-professional\\PyCharm 2019.3.3\\bin;',
	'PYCHARM_DISPLAY_PORT': '63342',
	'PYCHARM_HOSTED': '1',
	'PYTHONIOENCODING': 'UTF-8',
	'PYTHONPATH': 'X:\\Django\\request_data;Z:\\pycharm-professional\\PyCharm 2019.3.3\\plugins\\python\\helpers\\pycharm_matplotlib_backend;Z:\\pycharm-professional\\PyCharm 2019.3.3\\plugins\\python\\helpers\\pycharm_display',
	'PYTHONUNBUFFERED': '1',
	'SESSIONNAME': 'Console',
	'SYSTEMDRIVE': 'C:',
	'SYSTEMROOT': 'C:\\WINDOWS',
	'USERDOMAIN': 'LAPTOP-EI2EE90I',
	'USERDOMAIN_ROAMINGPROFILE': 'LAPTOP-EI2EE90I',
	'WINDIR': 'C:\\WINDOWS',
	'RUN_MAIN': 'true',
	'SERVER_NAME': 'LAPTOP-EI2EE90I',
	'GATEWAY_INTERFACE': 'CGI/1.1',
	'SERVER_PORT': '8011',
	'REMOTE_HOST': '',
	'CONTENT_LENGTH': '',
	'SCRIPT_NAME': '',
	'SERVER_PROTOCOL': 'HTTP/1.1',
	'SERVER_SOFTWARE': 'WSGIServer/0.2',
	'REQUEST_METHOD': 'GET',
	'PATH_INFO': '/app01/index/index_2/three_custom_tag/',
	'QUERY_STRING': 'username=egon',
	'REMOTE_ADDR': '127.0.0.1',
	'CONTENT_TYPE': 'text/plain',
	'HTTP_HOST': '127.0.0.1:8011',
	'HTTP_CONNECTION': 'keep-alive',
	'HTTP_PRAGMA': 'no-cache',
	'HTTP_CACHE_CONTROL': 'no-cache',
	'HTTP_UPGRADE_INSECURE_REQUESTS': '1',
	'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36',
	'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
	'HTTP_ACCEPT_ENCODING': 'gzip, deflate, br',
	'HTTP_ACCEPT_LANGUAGE': 'zh-CN,zh;q=0.9,en;q=0.8',
	'HTTP_COOKIE': 'csrftoken=ocjhW1fEWTsaZvJIXqi9gXB31Fu3mdoJORP0vqkY6aNnT7JCo0WzrcV92GTlSI7K; token_id="{\\"userID\\": 1}|43094b6783dcfe87b55d8d8277c3b784:1jqpyY:11xEFuh0GUKLdsXL3wU7Vit10hM"',
	'wsgi.input': < _io.BufferedReader name = 752 > ,
	'wsgi.errors': < _io.TextIOWrapper name = '<stderr>'
	mode = 'w'
	encoding = 'UTF-8' > ,
	'wsgi.version': (1, 0),
	'wsgi.run_once': False,
	'wsgi.url_scheme': 'http',
	'wsgi.multithread': True,
	'wsgi.multiprocess': False,
	'wsgi.file_wrapper': < class 'wsgiref.util.FileWrapper' > ,
	'CSRF_COOKIE': 'ocjhW1fEWTsaZvJIXqi9gXB31Fu3mdoJORP0vqkY6aNnT7JCo0WzrcV92GTlSI7K'
}

4.3 关于QueryDict的扩展

"""
1.QuerySet本质也是继承字典,但是比原生dict强大,这个QueryDict就不能被修改,改了就会抛出异常。
2.如果想要修改,就应该对QuerySet对象调用.copy()方法
"""
print(type(request.POST)) # <class 'django.http.request.QueryDict'>

from django.http.request import QueryDict

# QueryDict本质也是继承字典, 但是比原生dict强大, 这个QueryDict就不能被修改, 改了就会抛出异常.
request.POST.pop('username')  # AttributeError: This QueryDict instance is immutable

# 如果要想修改, 就应该对QueryDict对象调用.copy()方法.
new_query_dict = request.POST.copy()
print('new_query_dict:', new_query_dict)

print(new_query_dict.pop('username'))  # [''] (我们发现copy以后就可以修改了)

4.4 request.FILES方法补充

print('request.FILES:', request.FILES.get('avatar'), type(request.FILES), type(request.FILES.get('avatar')))
    from django.utils.datastructures import MultiValueDict
    from django.core.files.uploadedfile import InMemoryUploadedFile
    file_obj = request.FILES.get('avatar')
    print(file_obj.file,          # <_io.BytesIO object at 0x000001DCC1CAC0A0>
          file_obj.field_name,    # avatar
          file_obj.name,          # img2.jpg
          file_obj.content_type,  # image/jpeg
          file_obj.size,          # 3855
          file_obj.charset)       # None

五、FBV和CBV

5.1 FBV与CBV介绍与CBV基本使用

'''
FBV全称Function Based View CBV全称Function Based View. FBV和CBV各有千秋

CBV特点: 能够直接根据请求方式的不同直接匹配到对应的方法执行对应代码

FBV和CBV本质上是一样的,都是url地址绑定一个函数的内存地址
'''

# 路由层使用
    url(r'^my_index/', views.MyIndex.as_view())
    
# 视图层使用
    from django.views import View
    class MyIndex(View):
        def get(self, request):
            pass
        def post(self, request):
            pass

5.2 CBV源码剖析

加载时:views.MyView.as_view()
匹配前:views.view
匹配后:views.view(request, *args, **kwargs)

在我们所写的路由层里面,基于FBV写的视图函数都是通过直接书写函数的形式调用的,且调用的函数都是我们自己在视图层定义的。而基于CBV所写的视图函数需要通过调用我们所定义的类下面的方法来调用视图函数,且这里放置的是一个调用函数的形式。【突破点:as_view()】MyView是我们自己定义的一个类,但是我们没有定义as_view()这个绑定方法。同时还可以注意到调用绑定方法的时候没有传参,且是由一个类来调用的。可以大致猜测,as_view()这个绑定方法是@classmethod或者是@staticmethod。这个会到后面来验证。
在这里插入图片描述
定义的MyView是继承的View这个大类。根据属性查找,如果对象没有这个绑定方法,就去定义它的类那里找,还没有的话就去它的父类里面找。于是按住CTRL点击View查看View的源码。
在这里插入图片描述
通过查看源码发现调用as_view()这个函数的返回值仍然是一个函数,这就说明路由层书写的路由与视图函数的对相应关系的原则没有被打破。同时as_view()上面的类的绑定方法说明了前面的猜测没有猜错。
在这里插入图片描述
view的返回值是dispatch函数执行的返回值,再去看一下dispatch函数。dispatch函数返回地值是handler执行的结果,handler是通过获得request的请求方式来判断的。如果请求方式的小写是http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']这八种请求方式之一,handler就是通过反射获取到的request的请求方式,反射获取不到则抛出异常。如果请求方式不在八种请求方式中,同样抛出异常。
在这里插入图片描述
之前书写的MyView类中的get和post函数请求方式,请求方式的小写是在八种请求方式之一。所以传参(request, *args, **kwargs)调用,返回结果作为view函数的返回结果。就实现了浏览器是什么请求,执行视图层定义的MyView类中定义方法的函数。
在这里插入图片描述

5.3 CBV源码剖析流程(详细文字版)

第一步:

项目启动,urls.py脚本被加载。浏览器发出请求,由路由器进行筛选,当路由层匹配到了符和路由层定义的url后,会加括号调用执行绑定的视图层函数,当匹配到FBV定义的视图函数时,会直接调用;当匹配到了CBV定义的视图函数时,会拿着加载urls.py脚本时运行完函数的返回值加括号调用。

这里定义了一个类MyView,继承了View,调用了as_view(),将as_view()的返回值放在这做加括号调用。as_view()不是MyView定义的函数,根据属性查找顺序,对象找不到的去定义类里面找,再找不到的去父类里面找;同时,这个方法是由类调用的,没有进行其他传参,说明as_view()可能是一个静态方法或者是类方法,静态方法不需要传参,类方法由类调用,把自己当成第一个参数传入。

第二步:

进入View的源码找到了as_view()函数。as_view()函数是类方法,其返回值是一个名为view的函数,可以理解为执行完as_view()后放在这里的是views.view。view也是as_view内部定义的函数。view就是一个闭包函数,需要拿外部名称空间的变量。而view函数内部用到了cls,cls是调用as_view()的类,就是MyView,self是通过MyView实例化产生的对象。view函数的返回值是self对象的dispatch方法。

第三步:

又是面向对象的属性查找,找self,之前说self是MyView实例化的对象,到实例化的对象中查找,发现没有,去MyView类中查找,仍然没有,去View中查找,找到了。dispatch方法通过判断请求方式的小写在不在View大类定义的http_method_names中执行不同的函数。如果请求方式的小写在http_method_names中,就通过反射拿到这种请求方式对应的我们自己书写的函数方法,给请求方式对应的函数内存地址传参,将执行结果返回;如果请求方式为不在八种请求方式中或者无法获得这种请求方式则报错。dispatch的执行结果作为view函数的返回值。在MyView中定义了两种请求方式对应的代码,如果对应路由的请求方式是这两种请求方式,则可以执行对应代码,如果不在则无法执行。

六、settings.py配置信息

我们会发现settings.py里面的配置信息非常少,但是在django内部不可能只有这些配置信息或者说我们可以看到的是django暴露给用户的配置信息,内部由更加详细的配置信息。

# 这是暴露给用户的配置信息
from django.conf import settings
print(settings)
# <Settings "django5RoutingAssignment.settings">

# 这是项目内部所有的配置信息
from django.conf import global_settings
print(global_settings)
# <module 'django.conf.global_settings' from 'E:\\python 3.7\\lib\\site-packages\\django\\conf\\global_settings.py'>

通过比较我们可以得知项目内部全部的配置信息还包含了一个路径,这个路径就是全局配置信息的文件位置。会发现里面有非常多的配置信息(如下图,只截取了一部分)
在这里插入图片描述

无论django直接暴露给用户的配置信息还是内部全部的配置信息,所有的变量名都是大写形式,且暴露给用户的配置信息全部可以在全部配置信息中找到相同的配置信息。

  1. 为什么所有的配置信息都是大写?
  2. 为什么全部配置信息包含暴露给用户的配置信息?

基于前面CBV通过查看源码获得代码思路的例子,关于设置的问题同样通过查看源码来解决。

在这里插入图片描述
在这里插入图片描述
通过查看源码发现暴露给用户的settings是LazySettings这个类实例化的对象。
在这里插入图片描述

在查看源码之前,强化三种python内置函数/模块的用法:

1.os.environ查看当前文件的主目录
	import os
	print(os.environ)
	"""
	"E:\python 3.7\python.exe" E:/pycharm/django5RoutingAssignment/app01/views.py......
	"""

2.os.environ.get(key)获得environ获取到的字典中key对应的value


3.dir()获取括号内对象可以.出来的属性,包含自定义的和内置的

4.import lib.import_module(string)可以将字符串格式的数据转换成模块的路径导入
	import importlib
	mod = importlib.import_module("aaastr.bbb")
	print(mod)
	# <module 'aaastr.bbb' from 'E:\\pycharm\\django5RoutingAssignment\\aaastr\\bbb.py'>

在LazySettings这个类中,使用django的人可以在使用配置信息前自己手动配置settings。否则,django就会使用DJANGO_SETTINGS_MODULE指定的配置文件中的配置信息。
在这里插入图片描述
在从django环境变量中拿到了"DJANGO_SETTINGS_MODULE"对应的值后,将这个值作为参数传入,通过Settings这个类实例化了配置信息。这里传入的值是暴露给用户的settings.py中的配置信息。
在这里插入图片描述
无论是导入全局配置信息还是导入局部配置信息,都会先进行一个大写判断,如果变量是大写,才会进入后面的执行体,如果不是大写,就不会执行后面的执行体了。所以我们在settings.py中添加的配置信息的变量名必须全部大写才能生效。

导入全局配置文件的__init__方法,
第一步:先创造一个空对象,获取全局配置文件中的大写的配置信息变量名,利用反射setattr的方法给空对象添加属性。
第二步:利用import_module将用户自己配置的配置文件路径的字符串格式作为模块导入,用dir()将可以点获得属性整合起来,赋值给mod。
第三步:比如用户配置的配置信息,如果全局配置文件过了则覆盖,如果全局配置文件没有配置过则添加。最终实例化完成这个Settings类的对象,包含了全局配置文件中的配置信息和用户配置文件中的配置信息,重复的以用户的配置信息为准。

七、总结

# 三板斧
	三板斧:HttpResponse、render、redirect
	第一点:视图函数执行必须返回一个HttpResponse对象,render、redirect都继承了HttpResponse类
	第二点:render内部原理
		from templates import Template, Context
		temp = Template("<h1>{{ user }}</h1>")
		con = Context({'user':{'username':'jason', 'info':'jjjbaby'}})
		res = temp.render(con)
		return HttpResopnse(res)

# JsonResponse对象
	# json格式的作用:
		跨语言之间的数据传输,不同语言序列化后化成json这种中间格式,
		再转换成二进制进行网络传输,接收方收到了之后,再通过它的反序列化,就可以拿到属于自己的数据格式
	# 前后端序列化,反序列化对比:
		JSON.stringify		json_str = json.dumps()
		JSON.parse			json_bytes = json.loads(json_str)
	# JsonResponse使用
		from django.http import JsonResponse
		def index(request):
			# 方式一:传入字典
			'''
            内部原理:
            :param json_dumps_params: A dictionary of kwargs passed to json.dumps().  # 一个kwarg字典传递给json.dumps()。
            data = json.dumps(data, cls=encoder, **json_dumps_params)
            '''
            user_dic = {'username':'jijiji', 'hobbies':'read'}
            return JsonResponse(user_dic, json_dump_params={'ensure_sacii':False})

			# 方式二:传入非字典
			'''
            内部原理:
            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.'  # 为了允许非dict对象被序列化,设置safe参数为False。
            )
            '''
            li = [12,23,34]
            return JsonResponse(li, safe=False, json_dump_params={'ensure_ascii':False})

# form表单上传文件后端操作
	第一步:form表单的提交方式必须设置为post,enctyoe设置为multipart/from-data(上传文件的标签写上name='file')
	第二步:视图层使用FILES接收文件对象列表集合,使用get获取对象列表集合最后一个文件对象,使用getlist获取对象列表集合中所有文件对象
	def files(request):
		if request.method == 'POST':
			file_obj = request.FILES.get('file')
			'''
            内部实现原理:
            def chunks(self, chunk_size=None):
                self.file.seek(0)
                yield self.read()
            '''
			with open(file_obj.name,'wb') as f:
				for line in file_obj.chunks():
					f.write(line)
		return render(request,'.html')

# request对象方法
	request.method
	request.GET
	request.POST
	request.GET.get(key)
	request.GET.getlist()
	request.POST.get(key)
	request.POST.getlist()
	request.FILES				获取文件对象列表的集合
	request.body				获取原生浏览器发送过来的二进制数据
	request.path				获取路由
	request.path_info   		获取路由
	request.get_path_info()		获取路由+参数
	request.META				获取HTTP请求的其他剩余东西

# FBV和CBV
	FBV function based views
	CBV class based views
	CBV特点:内部原理能够直接根据请求方式的不同获得字符串,进而使用反射获取对应对象的方法
	# 路由层使用
	url(r'^my_index',views.MyIndex.as_view())
	# 视图层使用
	from django.views import View
	class MyIndex(View):
		def get(self, request):
			...
		def post(self, request):
			...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值