Django序列化
何为序列化,说白了就是能够将数据存到磁盘上,比如存到txt文件上。而什么能够被存到文件里面呢?字符串!So,序列化也就是将其他的数据类型转换为字符串的过程,那么在Django响应前端Ajax请求的时候,返回的数据是字符串类型的,那么如何将python中不同的类型转换成字符串类型呢?下面针对不同的情况提出了不同的解决办法
如果字典中有对象
众所周知,python的字典什么都能存,因此存一个两个对象自然不在话下,而有时候也确实有这样的需求。但是偏偏不巧,在使用 json.dump() 序列化字典的时候如果里面包含了对象就会报错,那么针对这问题应该怎样解决呢?解决办法就是使用 serializers ,如下
视图函数:
def index(request): ret = {'status': True, 'data': None} user_list = models.UserInfo.objects.all() ret['data'] = serializers.serialize("json", user_list) return render(request, "index.html", {'data': json.dump(ret)})
HTML页面:
$.ajax({ url: "/index", type: "post", dataType: 'json', // 在这里进行了一次反序列化 success:function(arg){ if(arg.status){ var ret = JSON.parse(arg.data); // 在这里进行了第二次反序列化 } } });
当前端接收到数据后会把字符串变成它原本的类型,这一过程称为反序列化,而上面由于进行了两次序列化的过程,因此在进行反序列化的时候也要两次,在使用 dataType:'JSON' 后,还要再将数据 JSON.parse 一次才行
如果字典中没有对象
如果在字典中没有出现对象,那么久更容易了,直接用 json.dump() 就可以了
视图函数:
def index(request): ret = {'status': True, 'data': None} user_list = models.UserInfo.objects.all().values('id', 'username') ret['data'] = list(user_list) return render(request, "index.html", {'data': json.dump(ret)})
HTML页面:
$.ajax({ url: "/index", type: "post", dataType: 'json', success:function(arg){ if(arg.status){ var ret = arg.data; // 获取到的数据直接使用 } } });
由于在序列化的时候只进行了一次,因此这次拿到的数据直接使用就好了
Django接收文件
现在的网站少不了的便是一个账号,既然有账号就得有头像什么的,那么就需要上传图片,那么在后台如何接收到用户上传的图片并进行保存呢?
在Django中,上传的文件的信息都保存在了 request.FILES 里面了,因此只需要去那里面获取就好
file = request.FILES.get('file') # 获取文件对象,包括文件名文件大小和文件内容 print(file.name) # 文件名 print(file.size) # 文件大小 # 将文件写入本地 f = open(file.name, 'wb') for line in file.chunks(): # 由于文件不是一次性上传的,因此一块一块的写入 f.write(line) f.close()
上面是自己写 <input type="file" /> 来上传文件,下面介绍使用Form来获取文件
相比于传统的方式,使用Form获取文件唯一的区别就是文件对象的位置不同,使用Form后文件保存到了 obj.cleaned_data 中,我们获取的时候去那里面获取,除此之外和上面的没有什么区别
class UploadForm(forms.Form): file = fields.FileField() def index(request): obj = UploadForm(request.POST, request.FILES) if obj.is_valid(): file = obj.cleaned_data['file'] print(file.name) # 文件名 print(file.size) # 文件大小 f = open(file.name, 'wb') for line in file.chunks(): f.write(line) f.close()
Ajax通过FormData上传文件
上面介绍了如何在后端接收文件,那么如何在前端上传文件呢?当然,上传文件还能用什么?不就是用 <input type="file" /> 吗?确实,只能用这个标签,但是这里是指怎样将文件发送给服务器,以何种方式?通常使用的是form表单提交,这是一种比较普遍的方式,但是使用表单的一个特点就是只要一提交就会刷新页面,那么有的时候不希望页面刷新应该怎么办呢?这就是Ajax的特性了,但是怎样通过Ajax发送文件呢?这就要用到浏览器中的一个对象——FormData了,首先介绍下FormData的使用,如下代码:
var data = new FormData(); // 获取FormData对象 data.append('k1', 'v1'); // 将数据以键值对的形式写入 data.append('k2', 'v2'); // 需要加上processData跟contentType $.ajax({ url: '', type: 'post', data: data, processData: false, // 告诉jQuery不要处理数据 contentType: false, // 告诉jQuery不要设置类型 success: function(arg){ console.log(arg); } });
由上面的代码可知,FormData跟一个字典差不多,都是以键值对存储数据,发送的时候将对象设置给ajax的data就好了,只是想要发送还要将jQuery的processData跟contentType都设置成false才行,那这么看这个FormData好像跟普通的json对象差不多,那要它何用?或者换句话说它的存在一定有其他的作用,那这个作用显然就是FormData能够上传文件了,那么究竟它要如何上传文件呢?见下面的一段小Demo就知道了。
<input type="file" id="file" /> <!-- 在这里上传文件 --> <script> var data = new FormData(); data.append('file', document.getElementById("file").files[0]); // 获取文件放入FormData中 // 发送数据时没有任何区别 $.ajax({ url: '/index.html', type: 'post', data: data, processData: false, // 告诉jQuery不要处理数据 contentType: false, // 告诉jQuery不要设置类型 success: function(arg){ console.log(arg); } }); </script>
由上面的Demo可知,无论是否上传文件,在使用Ajax发送的时候没有任何区别,那么唯一不同的就是在封装数据的那里需要获取上传文件的标签里面的文件内容,即 document.getElementById("file").files[0] , document.getElementById("file").files 获取的是该标签里面所有上传文件的内容,因为有的时候不只会上传一份文件
ifram+表单伪造Ajax请求
上面使用Ajax上传文件虽然好,但是仍旧有弊端,因为有的浏览器不支持FormData对象,因此为了更好的兼容性,还是使用表单会好一些,但是别忘了最初寻找Ajax方法上传文件的目的,因此有没有即能够解决兼容性问题又能够在上传后不刷新页面的方式呢?还真有,那就是使用iframe和表单来伪造Ajax请求,这样既解决了兼容性问题又不会刷新页面。那么究竟如何去做呢?其实很简单,将form的target属性与iframe的name属性关联即可
伪造Ajax发送数据
<ifram id="ifram" name="fram"></ifram> <!-- name="fram" target="from" --> <form id="fm" action="/ajax.html" method="post" target="from"> <input type="text" /> <input type="submit" value="提交" /> </form>
如此之后,再提交表单刷新的就是ifram了,而返回的数据也会在ifram中显示,但是既然是伪造Ajax,发送数据是伪造了,但是Ajax还有一个功能就是拥有一个回调函数,可以处理接收到的数据,那么怎样去伪造这个回调函数呢?
伪造回调函数
上面有说过,请求返回的内容都会在ifram中显示,那么在请求响应后去到ifram中获取数据然后处理不就可以伪造回调函数了吗?那么现在要解决的问题就是怎么确定请求的响应到达的时间,在ifram中有一个onload属性,在页面加载完毕后会触发,那么就可以在onload中去获取数据并处理
<ifram id="ifram" name="fram" onload="reloadIfram(this);"></ifram> <form id="fm" action="/ajax.html" method="post" target="from"> <input type="text" /> <input id="sub" type="submit" value="提交" /> </form> <script> function reloadIfram(ths){ // 使用原生js获取 ths.contentWindow.document.body.innerHTML; // 使用jquery获取 $(ths).contents().find('body').html(); } </script>
这样就可以在请求到达的时候执行 reloadIfram() 函数,做到模拟回调函数的效果,但是现在这样写有点问题,因为在第一次加载页面的时候,ifram加载完成会执行onload,但是由于页面从上到下加载,因此此时的 reloadIfram() 函数还没有被加载,因此会出现未定义的错误,虽然影响不大,但是能不能去解决这个错误呢?当然是可以的,思路就是在提交表单的时候再去为ifram绑定onload事件,那样就不怕reloadIfram()函数还没有加载了,具体代码如下:
document.getElementById("sub").click = function(){ document.getElementById("ifram").onload = reloadIfram; }