如何使Django表单在验证失败后保留文件

时间:2010-06-22 23:48:05

标签: django django-forms

form = AddItemForm(request.POST, request.FILES)

if form.is_valid()

   do_stuff

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

现在,form将包含错误信息以及字段的原始值,但它不会保留选定的文件 如果表单验证失败,如何保留所选文件?

5 个答案:

答案 0 :(得分:10)

您想要做的是,出于安全原因,浏览器不允许文件输入框在页面加载时具有预先选择的值。即使它只是保留同一页面的前一个实例的值,也是如此。 Django无法改变这一点。

如果您想避免要求用户重新选择并重新上传文件,即使验证失败,您也需要保存上传的文件,然后用一些内容替换文件输入字段以表明您已经拥有数据。如果用户想要放入不同的文件,您可能还需要一个运行某些JavaScript的按钮来重新启用文件字段。我不认为Django会为此提供任何机器,因此您必须自己编写代码。

答案 1 :(得分:10)

尝试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, 
        }

答案 2 :(得分:3)

好的,所以我首先投票并实现Narendra的解决方案,然后才意识到它无法正常工作,因为javascript无法设置输入类型=“文件”值。看到: http://www.w3schools.com/jsref/prop_fileupload_value.asp

但这是一个有效的解决方案:

  1. 将表单的其余部分与需要上传的文件分开。
  2. 始终保存文件(或运行验证以查看是否应保存文件)并保存到临时模型。
  3. 在模板中,有一个隐藏字段,表示临时模型实例的id,因此如果更改文件,则更改会传播。您可能会在服务器上保存额外的文件,但可以从外部进行清理。
  4. 当表格减去任何文件时可以保存,保存,将保存的文件绑定到原始模型并删除中间上传的文件模型。
  5. 好的,这是一个程序草图,对我来说是一个例子,你试图保存一本书的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>
    

答案 3 :(得分:2)

如果您正在使用modelForm并且您成功保存了具有fileField的该模型的新实例,Django将仅将文件自行保存到磁盘。

在您的情况下,您需要做的是从request.FILES字典中获取文件,并将其自行保存到磁盘。看起来应该是这样的。

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

现在您已将文件保存到磁盘,您只需记住该文件的路径,以便在用户重新提交失败的表单时再次打开它。

答案 4 :(得分:1)

有点棘手,但这是逻辑。

  1. 与您的HTML输入文件一起,有另一个隐藏字段。
  2. 编写一些JavaScript以保存此隐藏字段中的选定文件路径+名称。
  3. 在页面加载期间,您的JavaScript应检查隐藏字段的值,并将其设置为文件字段(如果有)。
  4. 首次加载时,隐藏字段为空,因此没有任何反应 在选择文件时,它会在隐藏字段中保存路径+名称 在第二次加载时,隐藏字段不为空,因此它将设置文件路径

    这样,所有脏工作都在浏览器中进行,除非您有有效的表单,否则您不必将文件保存在服务器上。