2010-06-22 25 views
18
form = AddItemForm(request.POST, request.FILES) 

if form.is_valid() 

    do_stuff 

return render_to_response(blah.html, {'form':form}) 

現已形成將與字段的原始值沿着錯誤信息後保留一個文件,但它不保留選定的文件 如何保持選定的文件如果表單驗證失敗?如何使一個Django表單驗證失敗

+0

我有同樣的問題。如果我使用模型字段,當驗證失敗時是否真的必須保存文件,以便驗證成功時可以上傳它?然後,我必須做一些垃圾回收,以刪除已上傳的文件,但用戶從未成功完成表單。我確信Django有這樣做的好方法! – PhoebeB 2010-09-07 09:35:59

回答

2

如果您使用的是modelForm,並且您成功保存了具有fileField的該模型的新實例,則Django將僅將文件保存到磁盤上。

在你的情況下,你需要做的是從request.FILES字典中獲取文件,並將其保存到磁盤上。它應該看起來像這樣。

input_file = request.FILES['fileformfieldname'] 
new_file = open('/path/to/file.xxx') 
new_file.write(input_file.read()) 

現在你已經保存到磁盤上的文件,你只需要記住的文件路徑,這樣當用戶重新提交失敗的形式,你可以再次打開它。

9

你想做什麼的問題是,出於安全原因,瀏覽器將不允許文件輸入框在頁面加載時具有預選值。即使它僅保留來自同一頁面的前一個實例的值,情況也是如此。 Django沒有辦法改變這一點。

如果您想避免要求用戶重新選擇並重新上傳文件,即使驗證失敗,也需要保存上傳的文件,然後用某些內容替換文件輸入字段以表明您已經有數據。如果用戶想要放入不同的文件,您可能還需要一個運行某個JavaScript的按鈕來重新啓用文件字段。我不認爲Django爲此提供了任何機制,因此您必須自己編寫代碼。

1

有點棘手,但這裏是邏輯。

  1. 隨着您的HTML輸入文件,有另一個隱藏的字段。
  2. 在此隱藏字段中寫入一些JavaScript以保存選定的文件路徑+名稱。
  3. 頁面加載期間,您的JavaScript應檢查隱藏字段的值,並將其設置爲「文件」字段(如果有)。

在第一次加載時,隱藏字段爲空,因此沒有任何反應。
在選擇文件時,它會將路徑+名稱保存在隱藏字段中。
在第二負載,隱藏字段不爲空,那麼它將設置文件路徑

這樣一來,所有的髒活累活發生在瀏覽器,除非你擁有有效的形式,你不必保存文件上服務器。

+6

這不會起作用。 Javascript無法更改輸入類型=文件值,或者惡意用戶可以將隨機猜測的文件名從計算機下載。 – 2011-04-28 22:13:19

3

好的,所以我首先提出並實現了Narendra的解決方案,然後才意識到它無法工作,因爲JavaScript無法設置輸入類型=「文件」值。請參閱: http://www.w3schools.com/jsref/prop_fileupload_value.asp

但這裏有一個可行的解決方案:形式

  1. 獨立的休息,從文件需要被上傳。
  2. 始終保存文件(或運行驗證以查看文件是否應保存)並保存到臨時模型。
  3. 在模板中,有一個隱藏字段說明臨時模型實例的ID,因此如果更改傳播更改文件。您可能會在服務器上保存額外的文件,但您可以在外部進行清理。
  4. 當表格減去任何文件可以保存時,保存它,將保存的文件綁定到最初的模型,並刪除中間上傳的文件模型。

好的,這裏是一個過程的草圖,它適用於我試圖用一個作者的電子郵件地址(選作電子郵件有時不驗證)保存書籍pdf的例子。

models.py

class Book(models.Model): 
    pdf = models.FileField("PDF", upload_to="books/") 
    author_email = models.EmailField("Author Email") 

class TempPDF(models.Model): 
    pdf = models.FileField("PDF", upload_to="books/") 

forms.py

from project_name.app_name.models import Book, TempPDF 
from django.forms import ModelForm 
from django.contrib.admin.widgets import AdminFileWidget 

class BookForm(ModelForm): 
    class Meta: 
     model = Book 
     exclude = ['pdf',] 

class TempPDFForm(ModelForm): 
    class Meta: 
     model = TempPDF 
     widgets = dict(pdf = AdminFileWidget) 
     # The AdminFileWidget will give a link to the saved file as well as give a prompt to change the file. 
     # Note: be safe and don't let malicious users upload php/cgi scripts that your webserver may try running. 

views.py

def new_book_form(request): 
    if request.method == 'POST': 
     ## Always try saving file. 
     try: 
      temp_pdf_inst = TempPDF.objects.get(id=request.POST.has_key('temp_pdf_id')) 
     except: ## should really catch specific errors, but being quick 
      temp_pdf_inst = None 
     temp_pdf_form = TempPDFForm(request.POST, request.FILES, instance=temp_pdf_inst, prefix='temp_pdf') 
     if temp_pdf_form.is_valid() and len(request.FILES) > 0: 
      temp_pdf_inst = temp_pdf_form.save() 
      temp_pdf_id = temp_pdf_inst.id 
     book_form = BookForm(request.POST, prefix='book') 

     if book_form.is_valid(): # All validation rules pass 
      book = book_form.save(commit=False) 
      book.pdf = temp_pdf_inst.pdf 
      book.save() 
      if temp_pdf_inst != None: 
       temp_pdf_inst.delete() 
      return HttpResponseRedirect('/thanks/') # Redirect after POST 
    else: 
     book_form = BookForm() # An unbound form 
     temp_pdf_form = TempPDFForm() 
     temp_pdf_id = None 
    return render_to_response('bookform_template.html', 
           dict(book_form = book_form, 
            temp_pdf_form = temp_pdf_form, 
            temp_pdf_id = temp_pdf_id) 
          ) 

bookform_template.html

<table> 
    {{ book_form }} 
    {{ temp_pdf_form }} 
    <input type="hidden" name="temp_pdf_id" value="{{ temp_pdf_id }}"> 
</table> 
9

嘗試django-file-resubmit

insallation

pip install django-file-resubmit 

settings.py

INSTALLED_APPS = { 
    ... 
    'sorl.thumbnail', 
    'file_resubmit', 
    ... 
} 

CACHES = { 
    'default': { 
     'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 
    }, 
    "file_resubmit": { 
     'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', 
     "LOCATION": project_path('data/cache/file_resubmit') 
    }, 
} 

使用

from django.contrib import admin 
from file_resubmit.admin import AdminResubmitMixin 

class ModelAdmin(AdminResubmitMixin, ModelAdmin): 
    pass 

from django.forms import ModelForm 
from file_resubmit.admin import AdminResubmitImageWidget, AdminResubmitFileWidget 

class MyModelForm(forms.ModelForm) 

    class Meta: 
     model = MyModel 
     widgets = { 
      'picture': AdminResubmitImageWidget, 
      'file': AdminResubmitFileWidget, 
     } 
+0

我第一次嘗試上傳時,文件消失。第二次嘗試是成功的。你有什麼解釋嗎? – Benjamin 2016-11-08 10:24:21