Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/django/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/365.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何使Django表单在验证失败后保留文件_Django_Django Forms - Fatal编程技术网

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

如何使Django表单在验证失败后保留文件,django,django-forms,Django,Django Forms,现在表单将包含错误信息和字段的原始值,但它不会保留选定的文件 如果表单验证失败,如何保留所选文件?如果您使用的是modelForm,并且您成功保存了该模型的一个新实例(其中包含一个文件字段),Django只会将该文件单独保存到磁盘 在您的情况下,您需要做的是从request.FILES字典中获取文件,然后自己将其保存到磁盘。应该是这样的 form = AddItemForm(request.POST, request.FILES) if form.is_valid() do_stuff

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

如果您使用的是modelForm,并且您成功保存了该模型的一个新实例(其中包含一个文件字段),Django只会将该文件单独保存到磁盘

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

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

if form.is_valid()

   do_stuff

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

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

问题在于,出于安全原因,浏览器不允许文件输入框在页面加载时具有预先选择的值。即使只是简单地保留同一页面上一个实例的值,这也是正确的。Django无法改变这一点


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

有点棘手,但这里是逻辑

  • 与HTML输入文件一起,还有另一个隐藏字段
  • 编写一些JavaScript将选定的文件路径+名称保存在此隐藏字段中
  • 在页面加载期间,JavaScript应该检查隐藏字段的值,并将其设置为文件字段(如果有)
  • 第一次加载时,隐藏字段为空,因此不会发生任何事情。
    选择文件时,它将路径+名称保存在隐藏字段中。
    在第二次加载时,隐藏字段不是空的,所以它将设置文件路径


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

    好的,所以我首先升级并实现了Narendra的解决方案,然后才意识到它无法工作,因为javascript无法设置输入type=“file”值。见:

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

  • 将表单的其余部分与需要上载的文件分开
  • 始终保存文件(或运行验证以查看是否应保存文件)并保存到临时模型
  • 在模板中,有一个隐藏字段,表示临时模型实例的id,因此如果更改文件,更改将传播。您可以在服务器上保存额外的文件,但可以从外部清理
  • 当表单减去任何文件都可以保存时,保存它,将保存的文件绑定到原始模型,并删除中间上传的文件模型
  • 好的,这里有一个程序的草图,在一个示例中,您试图保存一本带有作者电子邮件地址(电子邮件有时无法验证)的书的pdf

    models.py

    input_file = request.FILES['fileformfieldname']
    new_file = open('/path/to/file.xxx')
    new_file.write(input_file.read())
    
    forms.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/")
    
    views.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.
    
    bookform_template.html

    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_id = 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)
                                  )
    
    
    {{book_form}}
    {{temp\u pdf\u form}
    
    试试看

    嵌入

    <table>
      {{ book_form }}
      {{ temp_pdf_form }}
      <input type="hidden" name="temp_pdf_id" value="{{ temp_pdf_id }}">
    </table>
    
    设置.py

    pip install django-file-resubmit
    
    用法


    我有同样的问题。如果我使用的是模型字段,那么我真的必须在验证失败时保存该文件,以便在验证成功时上载它吗?然后,我还必须进行大量垃圾收集,以删除上载的文件,但用户从未成功完成表单。我相信Django有一个很好的方法!这是行不通的。Javascript无法更改输入类型=文件值,或者恶意用户可能会从您的计算机上下载随机猜测的文件名。我第一次尝试上载时,文件会消失。第二次尝试是成功的。你对此有什么解释吗?赋值前引用的局部变量“temp\u pdf\u id”
    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, 
            }