Django 验证错误后重新提交 ImageField 中的图像

发布于 2024-10-21 11:55:14 字数 1080 浏览 2 评论 0原文

我的管理界面中有一个带有 ImageField 的表单。一切都很好,除非其他字段引发验证错误。在这些情况下,表单将返回给用户进行更正,但已加载的图像文件将从表单中清除。

知道如何以某种方式将已提交的图像重新加载到表单中以便保存图像吗?

谢谢!

一些有趣的代码

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):
    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))

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):
    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))

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

太阳男子 2024-10-28 11:55:14

HTML 字段无法预先填充数据。因此,如果在另一个字段上验证失败,您将必须重新选择图像。

这是一种 HTML/浏览器安全措施。就像,没有办法解决它!

想象一下,如果一个网站可以将 C:\Windows\something_important 注入到页面一角的表单中会怎样?


如果这是关键功能,您可以...

  1. 强制在表单验证之前上传文件,并将文件名存储在用户会话中
  2. 设置模板,以便在文件上传后重新显示时显示“成功”消息
  3. 禁用文件当会话包含上传信息时进行字段验证
  4. 在提交真实有效的表单后从会话中提取文件名

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...

  1. Force the file to be uploaded before form validation and stash the file name in the user session
  2. Set up your template to display a "success" message on re-display if file is uploaded
  3. Disable file field validation when session contains upload info
  4. Pull the filename from your session upon true valid form submission
_蜘蛛 2024-10-28 11:55:14

这是 django 应用程序,称为 django-file-resubmit。它正好解决了这个问题。

https://github.com/un1t/django-file-resubmit

Here is django app, called django-file-resubmit. It solves exactly this problem.

https://github.com/un1t/django-file-resubmit

滿滿的愛 2024-10-28 11:55:14

如果验证失败,您需要发送图像文件作为响应的一部分,然后 JavaScript 应从那里继续。

# 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()

在模板中添加所需的 JavaScript 代码:

        {% 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 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
});

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
});
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文