Django进阶
Task1 了解请求
相信学过计网的大家对请求,尤其是B/S的HTTP请求大家应该都了解的差不多了。这里抛开计网的知识不谈,我们来解析一下。
其实Django的请求,或者说Django的请求对象我们在之前已经接触过,在Views中我们的函数都会有一个参数:request: Django.http.HttpRequest。这个请求我们可以差分出以下的属性:
path : 完整的请求路径
method: 请求方法 GET POST PUT DELETE
headers: accept: 接受的编码格式
cookies: 字典,包含所有的cookie参数
cookie中也包括session
meta: 客户端的元数据
我们来具体看一下请求对象,这样才能搞懂他的操作与原理的嘛
class HttpRequest:
"""A basic HTTP request."""
# The encoding used in GET/POST dicts. None means use default setting.
_encoding = None
_upload_handlers = []
def __init__(self):
# WARNING: The `WSGIRequest` subclass doesn't call `super`.
# Any variable assignment made here should also happen in
# `WSGIRequest.__init__()`.
# GET请求,POST请求结果是一个QueryDict对象
self.GET = QueryDict(mutable=True)
self.POST = QueryDict(mutable=True)
# Cookies, Meta是字典
self.COOKIES = {}
self.META = {}
self.FILES = MultiValueDict()
# 请求路径是字符串
self.path = ""
self.path_info = ""
self.method = None
self.resolver_match = None
self.content_type = None
self.content_params = None
根据Django的封装,我们来具象化Django可以接受的参数
- GET 查询参数
- POST 表单参数, 包好post / put两个请求方法的参数
- COOKIES 客户端当前domain的所有cookie信息
- MTEA 客户端的源信息
- REMOTE_ADDR
- FILES 上传的表单参数中所有的文件对象
- session 会话中存储的数据
- path 请求路径
- method 请求方法
- content_type 数据类型, 如text/html;
- content_params 编码类型, 如 charset = utf8
- body 字节码数据,一般接受上传的json数据
关于request的常用API直接在pychram中翻一翻就行了
Tips: URL 与 URI https://www.cnblogs.com/xy586/p/13281562.html?ivk_sa=1024320u
URI,是uniform resource identifier,统一资源标识符,用来唯一的标识一个资源。 Web上可用的每种资源如HTML文档、图像、视频片段、程序等都是一个来URI来定位的, URI说白了是给开发者用的,用于Rest规范
URL是uniform resource locator,统一资源定位器,它是一种具体的URI,即URL可以用来标识一个资源,而且还指明了如何locate这个资源。URL说白了是给客户访问用的。
TIPS: 关于请求,对于一个URL进行一个参数的多次赋值,最后会返回最后赋值的那个值。eg. weibo 微博搜索 (weibo.com)会在跳转到第四页的结果而不是前面的2,3页
Task2 PUT请求初尝试
咱就是说,HTTP的form 表单时只能使用GET 和POST方法,是没有办法使用PUT 与DELETE方式传参的,就算强用了,也会发生下面这样的事故:
<form action="http://127.0.0.1:8000/son1/hello" method="puts">
方法直接就变成GET了,那肯定不行。上网查了下,没办法,最简单的方式就是ajax。 其实我就没学过前端的,不过玩渗透的嘛,总是会接触一点点,所以尝试手搓一个ajax
<script>
function PUT_method() {
data = {
name: '爸爸,我是你儿子',
};
fetch('',{
method:'PUT',
headers:{
'Content_Type':"application/x-www-form-urlencoded"
},
body: JSON.stringify(data)
}).then(response => response.text()).then(html => {document.write(html)})
}
</script>
就是咱能看出Ajax能实现PUT的传播数据的。或者我们想串表单数据的话将data值更换即可
<script>
function PUT_method() {
data = {
name: document.forms[0].name.value
};
fetch('',{
method:'PUT',
headers:{
'Content_Type':"application/x-www-form-urlencoded"
},
body: JSON.stringify(data)
}).then(response => response.text()).then(html => {document.write(html)})
}
</script>
Task3 META数据初识
我们刚刚说过meta是存储访问的源信息。meta字段(元数据)
META是wsgi的请求元数据,包括服务起的环境信息,客户端的请求信息等。这玩应竟然包含了机子的环境变量等等各种适合渗透的信息。。。所以说,这玩应绝对不要放在你的模板上,不然机子都给你日穿了。
# views.py
@csrf_exempt
def put_try(request: HttpRequest):
print(request.META)
return render(request, 'post_try.html', locals())
# part_of output 我截取了部分wsgi的内容
{'wsgi.multithread': True,
'wsgi.multiprocess': False,
'wsgi.file_wrapper': <class 'wsgiref.util.FileWrapper'>,
'CSRF_COOKIE': 'sp838ShEhEcgi82TddA8Mk28PcC9rIvvJn8ALnxgsa77vsDaiZXJXpxnJId9rFRk'}
Task4 文件上传
首先我们要明确,GET和POST在传文件上面的区分。为社么我们要用POST传输文件呢:
- 首先:Post传输数据时,不需要在URL中显示出来,而Get方法要在URL中显示。
- 其次:Post传输的数据量大,可以达到2M,而Get方法由于受到URL长度限制,只能传递大约1024字节.
- 再次:Post就是为了将数据传送到服务器段,Get就是为了从服务器段取得数据.而Get之所以也能传送数据,只是用来设计告诉服务器,你到底需要什么样的数据.Post的信息作为http请求的内容,而Get是在Http头部传输的
也就是说,GET真的只适合请求,求求不要用GET来进行文件传输了。好,接下来我们先把前端给冲了
#在form中一定要设置type为文件上传类型,不然收不到文件
<form action="http://127.0.0.1:8000/upload" method="post", enctype=”multipart/form-data”>
<p>这是一个文本框<input name = 'text',value="你好,天龙人",type="text"/></p>
# 在HTML中上传文件使用的是input type = file标签即可
<p>上传文件这里请<input name = 'file', type="file"/></p>
<button type="submit", value="Submit">提交</button>
</form>
题外话,咱就是说套模板套久了自己写个form是真的太丑了看不下去 好的,我们来看后端。在request中,虽然文件使用POST上传的,但是文件并没有放在request.POST这个QuerySet()里面。他里面放的是文件名(真的是笑死我了)。我们想要调用文件的时候,需要用到request.FIFE这个东西。
m = request.POST.get('file',None)
print(type(m))
<class 'str'>
俗话说得好,遇到不懂的就看源码,我们直接找request.FILES对象嘛。你看我们上面的源码,然后能看到文件是一个MultiValueDict()对象。至于MulyivalueDict, 我们反倒定义,开头头一句就是个class继承dict。那差不多也和字典没啥区别了呀,下面已经可以不用看了。
# 看的出来,自己覆写的字典子类,那我们其实本质上还是自定义功能的字典嘛,就把它当字典用
class MultiValueDict(dict):
def __init__(self, key_to_list_mapping=()):
super().__init__(key_to_list_mapping)
我们接下来需要搞定文件的类型。
# views.py
def file_upload_deemo(request: HttpRequest):
if request.method == "GET":
return render(request, 'upload_deemo.html', locals())
else:
n = request.FILES.get('file', '123')
print(type(n))
return HttpResponse('Hello')
<class 'django.core.files.uploadedfile.InMemoryUploadedFile'>
这个东西,看名字就知道,是一个存在内存里的文件。这个类叫InMemoryUploadedFile。 我们想验证文件类型的时候,使用MIME这个协议的接口进行验证(因为它文件转换使用的是MIME)
# 限制文件类型。这个type的字符串我们可以查MIME的type
if n.content_type.startswith('image/'):
存文件的时候我们最好分块存储。文件本身给我们这个借口了,不然大文件那就啥也干不了的卡死了,IO始终是计算机的瓶颈痛点。至此,我们就完成了简单的文件上传。
# 用wb参数是因为他是2进制文件
with open('路径' + n.name, 'wb') as writer:
# 分段写入
for chuck in n.chunks():
writer.write(chuck)
writer.flush()
return HttpResponse('上传成功')
Task5 了解Response对象
响应对象是视图函数返回的对象,由我们自己创建。 我们在写views的时候返回的哪个东西就叫做response响应对象。比如说render, redirect等等返回值,常见的响应对象类 :
- HttpResponse
- HttpResponseRedirect
- JsonResponse
响应对象可以包含的属性:
- content: 响应的内容,或者说渲染后的模板
- status: 响应的状态码,快去复习
- content_type 响应的数据类型(http, img等等等等)