Resubmitting Image in ImageField after Validation Error in Django
I have, in my admin interface, a form with a ImageField. Everything works great except when some other field raises a validation error. In those cases, the form returns to the user for correction, but the already loaded image file is cleared from the form.
Any idea in how to, somehow, reload the already submitted image to the form in order to allow the image beeing saved?
Thanks!
SOme interesting bits of code:
class DealForm(forms.ModelForm):
image = forms.ImageField(required=False,widget=AdminImageWidget)
def clean():
data = self.cleaned_data
date_start = data.get('date_start')
date_end = data.get('date_end')
(... several other validations ...)
return data
.
class AdminImageWidget(forms.FileInput)开发者_JAVA百科:
def __init__(self, attrs={}):
super(AdminImageWidget, self).__init__(attrs)
def render(self, name, value, attrs=None):
output = []
if value and hasattr(value, "url"):
output.append(('<a target="_blank" href="%s">'
'<img src="%s" /></a> '
% (value.url, value.url_200x150)))
output.append(super(AdminImageWidget, self).render(name, value, attrs))
return mark_safe(u''.join(output))
HTML <input type="file">
fields cannot be prepopulated with data. Therefore, if validation fails on another field, you will have to re-select the image.
It's a HTML / browser security measure. As in, no way around it!
Imagine if a site could inject C:\Windows\something_important
into a form in the corner of the page?
If this is critical functionality, you could...
- Force the file to be uploaded before form validation and stash the file name in the user session
- Set up your template to display a "success" message on re-display if file is uploaded
- Disable file field validation when session contains upload info
- Pull the filename from your session upon true valid form submission
Here is django app, called django-file-resubmit. It solves exactly this problem.
https://github.com/un1t/django-file-resubmit
If validation fails you need to send the image file as part of the response then JavaScript should continue from there.
# if an image is loaded convert it to base64 string and include it in the context
if 'tnimagefn' in self.request.FILES:
ctx['img'] = base64.b64encode(self.request.FILES.get('tnimagefn').read()).decode()
in your template add the required JavaScript code:
{% if img %}
// create a dataURL from your base64 string and make it the source of image element
let imgdataURL = "data:image/png;base64,{{ img }}"
document.getElementById("tnimage").src = imgdataURL;
//convert the dataURL to a blob
let blob = dataURItoBlob(imgdataURL);
// create a file of that blob and associate with your file input element
let list = new DataTransfer();
let cImageFile = new File([blob], "tnimg.png");
list.items.add(cImageFile);
document.getElementById("id_tnimagefn").files = list.files;
{% endif %}
dataURItoBlob function:
function dataURItoBlob(dataURI) {
if(typeof dataURI !== 'string'){
throw new Error('Invalid argument: dataURI must be a string');
}
dataURI = dataURI.split(',');
var type = dataURI[0].split(':')[1].split(';')[0],
byteString = atob(dataURI[1]),
byteStringLength = byteString.length,
arrayBuffer = new ArrayBuffer(byteStringLength),
intArray = new Uint8Array(arrayBuffer);
for (var i = 0; i < byteStringLength; i++) {
intArray[i] = byteString.charCodeAt(i);
}
return new Blob([intArray], {
type: type
});
精彩评论