那些年,我們在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上傳文件案例,可聯系本人,謝謝!

 

  

   


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM