那些年,我们在Django web开发中踩过的坑(一)——神奇的‘/’与ajax+iframe上传
一、上传图片并在前端展示
为了避免前端整体刷新,我们采用ajax+iframe(兼容所有浏览器)上传,这样用户上传之后就可以立即看到图片:
上传前:
上传后:
前端部分html:
1 2 3 4 5 6 7 8 9 | < form style="display: inline-block" id="upload_img_form" name="form" action="/upload/" method="POST" enctype="multipart/form-data"> {% csrf_token %} < a id="fakeFile" class="fake-file"> < input type="file" name="img" onchange="UploadImage(this);"/> < input type="text" name="url" class="hide"/> </ a > < iframe id='upload_img_iframe' name='upload_img_iframe' src="" class="hide"></ iframe > </ form > |
别看这么短短的html,坑却不少:
- 坑one:action="/upload/",当年笔者傻傻的就只写了action="/upload",因为url是这么配的:url(r'^upload', v.upload),结果
django居然报错了,笔者查了很多资料,最后才知道,原来,虽然url的最后没有加'/',但是django在运行时会自动给url的后面加'/',因此,我们在post提交时,必须在url的最后加'/'。
- 坑two:enctype="multipart/form-data",文件上传相对于其他表单类型出现的概率比较小,而文件上传确是这些表单类型中的异类,它需要在form写上enctype="multipart/form-data"。
- 坑three:{% csrf_token %},django运行程序时,请求首先会通过中间件,然后才会通过url,django配置文件中有关于跨站请求伪造的中间件:
12345678910
MIDDLEWARE_CLASSES
=
[
'django.middleware.security.SecurityMiddleware'
,
'django.contrib.sessions.middleware.SessionMiddleware'
,
'django.middleware.common.CommonMiddleware'
,
<strong>
'django.middleware.csrf.CsrfViewMiddleware'
,<
/
strong>
'django.contrib.auth.middleware.AuthenticationMiddleware'
,
'django.contrib.auth.middleware.SessionAuthenticationMiddleware'
,
'django.contrib.messages.middleware.MessageMiddleware'
,
'django.middleware.clickjacking.XFrameOptionsMiddleware'
,
]
django为用户实现防止跨站请求伪造的功能,通过中间件 django.middleware.csrf.CsrfViewMiddleware 来完成。而对于django中设置防跨站请求伪造功能有分为全局和局部。
全局:
中间件 django.middleware.csrf.CsrfViewMiddleware
局部:
- @csrf_protect,为当前函数强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
- @csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。
中间件'django.middleware.csrf.CsrfViewMiddleware',会查看post请求是否携带token ,如果没有则直接在process_request中return 并报出如下错误:
此时,我们有几种处理方式:1.直接简单粗暴的注释掉settings中的csrf中间件;2.给接收post请求的views函数加@csrf_exempt(注:from django.views.decorators.csrf import csrf_exempt,csrf_protect),该装饰器的意思是取消当前函数防跨站请求伪造功能;
JavaScript代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | function UploadImage(ths) { document.getElementById( 'upload_img_iframe' ).onload = UploadImageComplete; //页面加载完成后执行UploadImageComplete函数 document.getElementById( 'upload_img_form' ).target = 'upload_img_iframe' ; //设置form提交到iframe document.getElementById( 'upload_img_form' ).submit(); //#提交到iframe } function UploadImageComplete() { var origin = $( "#upload_img_iframe" ).contents().find( "body" ).text(); //#获取图片数据 var obj = JSON.parse(origin); //#转换成JavaScript对象 var img = document.createElement( 'img' ); //#创建img标签 img.src = obj.path; //图片地址 img.style.width = "200px" ; img.style.height = "180px" ; $( "#upload_img_form" ).append(img); //添加图片 $( '#fakeFile' ).addClass( 'hide' ); $( '#reUploadImage' ).removeClass( 'hide' ); $( '#fakeFile' ).find( 'input[type="text"]' ).val(obj.data); //#保存图片地址到隐藏的input标签中 } |
后台views函数代码:
1 2 3 4 5 6 7 8 9 10 11 12 | def upload(request): if request.method = = 'POST' : obj = request.FILES.get( 'img' ) #获取图片对象 pat = os.path.join( 'static' , 'img' , 'vote' ,obj.name) #文件打开目录,需要和当前文件路径一致 f = open (pat, 'wb' ) for ch in obj.chunks(): f.write(ch) f.close() ret = { 'path' : '/' + os.path.join( 'static' , 'img' , 'vote' ,obj.name)} #django前端文件路径:'/' + 静态文件前缀static + 静态文件下的目录 import json return HttpResponse(json.dumps(ret)) #反馈给前端 return render(request, 'upload.html' ,) |
后台和前端js关于文件路径再现一坑:
注:这里我们将图片保存在静态文件目录static下的img中(静态文件路径的前缀也是static)
django前端我们希望的引入图片的路径是这样的:/static/img/xx.png,这里需要注意的是static前必须加'/',没错,就是这个神奇的'/',代表当前程序主目录,即配置文件中的BASE_DIR。
后台我们打开文件写入图片到服务器的路径,这里open路径需要遵循的是python的规则而不是django前端的规则,即python会默认从当前路径开始找,因此直接static/img/xx.png就行,前面不需要加'/'。
二、数据库查询结果给前端反馈序列化的问题
json不能序列化django中的datatime、Decimal等数据结构,解决方案:
1.单独转为python数据结构
1 | str ( Decimal( '12.36' )) |
然后通过json进行序列化。
2.直接将数据库查询结果QuerySet对象转化为列表,QuerySet看起来像列表
1 | ret = list (QuerySet对象)<br>result = json.dumps(ret)<br> |
由于json.dumps时无法处理datetime日期,所以可以通过自定义处理器来做扩展,如:
import json from datetime import date from datetime import datetime class JsonCustomEncoder(json.JSONEncoder): def default(self, field): if isinstance(field, datetime): return o.strftime('%Y-%m-%d %H:%M:%S') elif isinstance(field, date): return o.strftime('%Y-%m-%d') else: return json.JSONEncoder.default(self, field) # ds = json.dumps(d, cls=JsonCustomEncoder)
这是通过制定json.dumps序列化的类来实现的。
3.使用django的序列化工具serializers
1 2 3 4 5 | from django.core import serializers ret = models.BookType.objects. all () data = serializers.serialize( "json" , ret) |
总结:常见的form表单传文件可能大家都会,因此本博文主要讲述了ajax+iframe上传文件中需要注意的一些问题,如需form上传文件案例,可联系本人,谢谢!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· 展开说说关于C#中ORM框架的用法!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?