一、上传文件相关
请求头ContentType
ContentType指的是请求体的编码类型,常见的类型共有3种:
application/x-www-form-urlencoded
最常见的 POST 提交数据的方式了
浏览器的原生 form 表单,如果不设置?enctype?属性,那么最终就会以 默认格式application/x-www-form-urlencoded 方式提交数据,ajax默认也是这个
urlencoded是一种数据格式,
比如:
username=yang&password=123&csrfmiddlewaretoken=.......
# 这样的一种格式,类似get请求提交数据的格式
# 这样在后台,就可以通过request.POST,就可以直接拿到数据了
multipart/form-data
专门用来传输文件的消息格式,会把文件作为片段数据发送,而不是整个文件一起发送
application/json
告诉服务端消息主体是序列化后的 JSON 字符串
之前给ajax传递数据,就是把字典写成了一个类似json类型的数据
def login(request):
...
ret = ‘{"code":0,"redirect_url":"/index/"}‘
return HttpResponse(ret)
在不加content_type=‘application/json‘时,传递过去的就是一个字符串
def login(request):
d1 = {"code":0,"redirect_url":"/index/"}
import json
d1_json = json.dumps(d1)
return HttpResponse(d1_json,)
success:function (res) {
console.log(res,typeof res);
# {"code": 0, "redirect_url": "/index/"} string
....
}
这个时候,就不方便我们后续的使用,我们期望还是能拿到一个字典(自定义对象的类型)
使用content_type=‘application/json‘,ajax就会调用自己内部的反序列化机制,直接调用反序列化方法
def login(request):
d1 = {"code":0,"redirect_url":"/index/"}
import json
d1_json = json.dumps(d1)
return HttpResponse(d1_json,content_type=‘application/json‘)
success:function (res) {
console.log(res,typeof res);
# {code: 0, redirect_url: "/index/"} "object"
....
}
此时,ajax端不需要自己序列化,也拿到了object类型的对象,这是因为内部自动调用的
application/json 在ajax端发送数据,我们如何在视图函数中应用
$.ajax({
url:‘{% url "data" %}‘,
type:‘post‘,
data:JSON.stringify({k1:‘v1‘,k2:‘v2‘}), # 序列化成字符串
contentType:‘application/json‘, # 指定类型
....
})
3 此时再
def data(request):
print(request.POST) # ,拿不到数据
print(request.body) # b'{"k1":"v1","k2":"v2"}',要从原始的数据拿
return HttpResponse('ok')
b'{"k1":"v1","k2":"v2"}'
总之:
在django内部,对于content-type是application/x-www-form-urlencoded的,可以解析,通过request.POST.get()就能取值
在django内部,对于content-type是application/json类型的,是不能解析的,需要再request.body中取原始的数据,再反序列化,才能拿到数据
content-type就是一个数据格式,后端针对不同的数据格式进行不同的解析,默认是urlencoded的,django可以解析
JsonResponse
JsonResponse是HttpResponse的子类,专门用来生成JSON编码的响应
此时传递数据的时候,既不需要自己做序列化,也不需要指定content_type=‘application/json‘,只需要把数据直接放在JsonResponse里,就会自动帮你转换成json字符串,并且自动拼接content_type=‘application/json‘,
from django.http import JsonResponse # 需要先导入
def login(request):
d1 = {"code":0,"redirect_url":"/index/"}
return JsonResponse(d1)
# 注意:字典类型可以直接JsonResponse(d1),若不是字典,是列表等,就要JsonResponse(d1,safe=False)
success:function (res) {
console.log(res,typeof res);
# {code: 0, redirect_url: "/index/"} "object"
....
}
此时,也仍然拿到了object类型的数据
例子:
需求:用户输入127.0.0.1:8000/home,通过路径返回home.html文件,home.html文件中用ajax从data路径得到数据,然后在自己页面显示成一个列表显示出来
# 配置路径:
url(r'^home/', views.home,name='home'),
url(r'^data/', views.data,name='data'),
# 视图views:
def home(request):
return render(request,'home.html')
def data(request):
# l1 = ['故人西辞黄鹤楼','烟花三月下扬州','孤帆远影碧空尽','唯见长江天际流']
l1 = [11,22,33,44]
return JsonResponse(l1,safe=False) # 列表要加safe
# home.html:
可见,ajax通过异步请求,在data路径拿到了数据组成了li标签,然后放在了ul标签下。
自动拼接content_type='application/json',可以从网页的Network下的XHR观察出来,XHR下的请求就是ajax请求
form表单上传文件
配置路径 url(r‘^upload/‘, views.upload,name=‘upload‘),
写html,就一个表单提交数据,文件提交的(input属性是type="file")
{% csrf_token %}
头像:
用户名:
写视图函数
def upload(request):
if request.method == ‘GET‘:
return render(request,‘upload.html‘)
else:
print(request.POST)
# 拿到的是post请求的数据,文件相关数据去request.FILES拿
return HttpResponse(‘ok‘)
打印的request.POST,数据如下:
从得到的数据可以发现,request.POST得到的文件数据就是一个名字,没有任何意义
文件的数据,都在request.FILES中
# 在form表单里,要先指定消息格式
# 默认的请求消息格式是urlencoded,携带不了文件数据
# 要指定multipart/form-data才可以携带数据
......
def upload(request):
if request.method == 'GET':
return render(request,'upload.html')
else:
print(request.FILES)
return HttpResponse('ok')
# 如果form表单没有指定文件的消息格式,接收不到数据,就得到一个空字典:
# 此时再打印request.FILES就拿到了上传了文件的数据:
]}>
把得到的文件数据读取出来,并保存到本地
def upload(request):
if request.method == ‘GET‘:
return render(request,‘upload.html‘)
else:
file_obj = request.FILES.get(‘head_pic‘)
file_name = file_obj.name
with open(file_name,'wb') as f:
for i in file_obj:
f.write(i)
return HttpResponse('ok')
以上方法上传的文件没有指定路径,就直接保存在了项目的文件夹里;此时想要把上传的文件保存在指定的文件夹中,比如项目下的statics中的img文件夹中
from ajax_pro import settings
import os
def upload(request):
if request.method == 'GET':
return render(request,'upload.html')
else:
file_obj = request.FILES.get('head_pic')
file_name = file_obj.name
path = os.path.join(settings.BASE_DIR,'statics','img',file_name)
# 拼接路径
with open(path,'wb') as f:
for i in file_obj:
f.write(i)
# 每次读的文件不是固定长度,每次读一行,识别符为\r,\n,,\r\n,遇到这几个符号就算是读了一行
return HttpResponse('ok')
另一种保存文件的方法:
from ajax_pro import settings
import os
def upload(request):
if request.method == 'GET':
return render(request,'upload.html')
else:
file_obj = request.FILES.get('head_pic')
file_name = file_obj.name
path = os.path.join(settings.BASE_DIR,'statics','img',file_name)
# 拼接路径
with open(path,'wb') as f:
for chunck in file_obj.chunks():
f.write(chunck)
# 固定长度读取,默认一次读65536B,也就是64KB,2.5M,是一个生成器
return HttpResponse('ok')
ajax上传文件
如何获取当前文件对象的数据:$(‘#file‘)[0].files[0]
后端的读取文件并保存的代码跟form的文件上传一样
前端的html文件,用到ajax
ajax上传头像:
ajax用户名:
提交
{% csrf_token %}
根据以前写ajax代码的习惯,会这样写,但是会报错,因为文件上传要依靠FormData这个工具
正确写法:
ajax上传头像:
ajax用户名:
提交
{% csrf_token %}
注意点:
ajax文件上传需要借助FormData,给它填键值对的方式传输数据
data直接传formdata,但是后面要加processData:false和contentType:false
JSON 机制
JSON 指的是 Javascript 对象表示法(Javascript Object Notation)
JSON 是轻量级的文本数据交换格式
JSON 独立于语言 *
JSON 具有自我描述性,更易理解
json和python数据类型对比:
但是,json在转换时间类型的时候,会出现问题:
import datetime
import json
d = {'name':'yang','birthday':datetime.datetime.now()}
json_str = json.dumps(d)
print(json_str)
# 报错,TypeError: Object of type 'datetime' is not JSON serializable,意思是'datetime'类型的是不能序列化的
json序列化时间日期类型的数据的方法
from datetime import date,datetime
import json
#对含有日期格式数据的json数据进行转换
class JsonCustomEncoder(json.JSONEncoder):
# 序列化的功能就是json.JSONEncoder这个类做的,因为它不能序列化时间,所以继承他的情况下进行拓展
def default(self, field):
if isinstance(field,datetime):
# 如果是datetime类型的,返回一个datetime类型的字符串格式化输出
return field.strftime('%Y-%m-%d %H:%M:%S')
elif isinstance(field,date):
# 如果是date类型的,返回一个date类型的字符串格式化输出
return field.strftime('%Y-%m-%d')
else:
# 如果不是date、datetime类型的,就用父类的方法
return json.JSONEncoder.default(self,field)
d1 = datetime.now()
json_str = json.dumps(d1,cls=JsonCustomEncoder)
print(json_str) # "2019-07-29 21:08:25"
十、Django的文件上传