15đź‘Ť
The problem with what you want to do is that, for security reasons, browsers will not allow a file input box to have a pre-selected value on page load. This is true even if it is simply preserving the value from a previous instance of the same page. There is nothing Django can do to change this.
If you want to avoid asking the user to re-select and re-upload the file, you will need to save the uploaded file even when validation fails, and then replace the file input field with something to indicate that you already have the data. You would also probably also want a button that runs some JavaScript to re-enable the file field if the user wants to put in a different file. I don’t think Django comes with any machinery for this, so you’ll have to code it yourself.
12đź‘Ť
installation
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')
},
}
usage
from django.contrib import admin
from file_resubmit.admin import AdminResubmitMixin
class ModelAdmin(AdminResubmitMixin, ModelAdmin):
pass
or
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,
}
- [Django]-Django error: got multiple values for keyword argument
- [Django]-Django check if object in ManyToMany field
- [Django]-Python Asyncio in Django View
3đź‘Ť
Ok, so I first upvoted and implemented Narendra’s solution before realizing it couldn’t work as javascript can’t set input type=”file” values. See:
http://www.w3schools.com/jsref/prop_fileupload_value.asp
But here’s a solution that works:
- Separate the rest of the form, from the files needing to be uploaded.
- Always save the file (or run the validation to see if the file should be saved) and save to temporary model.
- In the template, have a hidden field saying the id of the instance of the temporary model so if you change the file the change propagates. You may get extra files saved on your server, but you can clean up externally.
- When the form minus any files can be saved, save it, bind the saved files to the originally model and delete the intermediate uploaded file model.
Ok, here’s a sketch of the procedure, which works for me of an example where you are trying to save a pdf of a book with an email address (chosen as emails sometimes do not validate) of an author.
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_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)
)
bookform_template.html
<table>
{{ book_form }}
{{ temp_pdf_form }}
<input type="hidden" name="temp_pdf_id" value="{{ temp_pdf_id }}">
</table>
- [Django]-Django and fieldsets on ModelForm
- [Django]-Django templates: Best practice for translating text block with HTML in it
- [Django]-Django Server Error: port is already in use
2đź‘Ť
Django will only save the file to disk on its own if you are using a modelForm and you successfully save a new instance of that model which has a fileField.
In your case what you have to do is get the file from the request.FILES dictionary, and save it to disk on your own. It should look something like this.
input_file = request.FILES['fileformfieldname']
new_file = open('/path/to/file.xxx')
new_file.write(input_file.read())
Now that you have the file saved to disk, you just have to remember the path to the file, so you can open it again when the user resubmits the failed form.
- [Django]-How to send a POST request using django?
- [Django]-Success_url in UpdateView, based on passed value
- [Django]-Timestamp fields in django
-1đź‘Ť
Bit tricky but here is the logic.
- Along with your HTML Input File, have another hidden field.
- Write some JavaScript to hold selected file path + name in this hidden field.
- During page load, Your JavaScript should check hidden field’s value and set that to File field, if it has any.
On first load, hidden field is empty, so nothing happens.
On selection of file, it saves path+name in hidden field.
On second load, hidden field is not empty, so it will set file path
This way, all the dirty work happens in Browser, and Unless you have valid form, you don’t have to save the file on server.
- [Django]-How do I execute raw SQL in a django migration
- [Django]-IOError: request data read error
- [Django]-In PyCharm, how to navigate to the top of the file?