• 那些年,我们在Django web开发中踩过的坑(一)——神奇的‘/’与ajax+iframe上传


    一、上传图片并在前端展示

    为了避免前端整体刷新,我们采用ajax+iframe(兼容所有浏览器)上传,这样用户上传之后就可以立即看到图片:

    上传前:

    上传后:

    前端部分html:

    <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配置文件中有关于跨站请求伪造的中间件:
      MIDDLEWARE_CLASSES = [
          'django.middleware.security.SecurityMiddleware',
          'django.contrib.sessions.middleware.SessionMiddleware',
          'django.middleware.common.CommonMiddleware',
          'django.middleware.csrf.CsrfViewMiddleware',
          '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代码: 

    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函数代码:

    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数据结构

    str( Decimal('12.36'))
    

      然后通过json进行序列化。

    2.直接将数据库查询结果QuerySet对象转化为列表,QuerySet看起来像列表

    ret=list(QuerySet对象)
    result=json.dumps(ret)

      由于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

    from django.core import serializers
     
    ret = models.BookType.objects.all()
     
    data = serializers.serialize("json", ret)
    

      总结:常见的form表单传文件可能大家都会,因此本博文主要讲述了ajax+iframe上传文件中需要注意的一些问题,如需form上传文件案例,可联系本人,谢谢!

      

       

  • 相关阅读:
    Docs-.NET-C#-指南-语言参考-预处理器指令:#error(C# 参考)
    Docs-.NET-C#-指南-语言参考-预处理器指令:#warning(C# 参考)
    Docs-.NET-C#-指南-语言参考-预处理器指令:#undef(C# 参考)
    Docs-.NET-C#-指南-语言参考-预处理器指令:#define(C# 参考)
    Docs-.NET-C#-指南-语言参考-预处理器指令:#endif(C# 参考)
    Docs-.NET-C#-指南-语言参考-预处理器指令:#elif(C# 参考)
    Docs-.NET-C#-指南-语言参考-预处理器指令:#else(C# 参考)
    Docs-.NET-C#-指南-语言参考-预处理器指令:#if 预处理指令
    Docs-.NET-C#-指南-语言参考-预处理器指令:C# 预处理器指令
    1212 最大公约数
  • 原文地址:https://www.cnblogs.com/wanghzh/p/5817518.html
Copyright © 2020-2023  润新知