【问题标题】:Display image from ImageField by means of form以表单的方式显示来自 ImageField 的图像
【发布时间】:2015-04-30 03:15:20
【问题描述】:

前奏

Here 是显示 ImageField 的最简单方法。假设我的表单中有 10 个字段,我不想在 template.html 中遍历所有字段,只是为了检查它是否是一个 imageField 并以不同的方式显示它。我想通过表单或小部件或this 之类的东西来处理它。所以我想要离开 template.html,如下所示。

template.html

<table>
    {{ form.as_table }}
</table>

并在 models.py 中添加一个 image_tag 方法:

class UserProfile(Model):
  photo = ImageField(upload_to=PHOTO_DIRECTORY)
  contacts = TextField(null=True)
  ... others

  def image_tag(self):
    return u'<img src="%s" />' % self.photo.url

  image_tag.short_description = 'Image'
  image_tag.allow_tags = True

forms.py

class UserProfileForm(forms.ModelForm):

  class Meta:
    model = UserProfile
    fields = ('contacts', 'photo', 'image_tag', ...others)

我收到一个错误,Unknown field(s) (image_tag) specified for UserProfile 这很公平。

我也尝试将它放在readonly_fields 中,但它们不会以某种方式显示。我必须创建list_view 吗?如果是,应该在哪里?

我在这里想念什么?为什么image_tag 方法应该是模型而不是表单的一部分?模型描述了数据应该如何保存在数据库中,而不是它应该如何通过表单呈现给用户。这应该是表单的一部分,不是吗?如果我错了,请纠正我。我该如何解决这个问题?任何帮助将不胜感激!

【问题讨论】:

  • 您不能在 ModelForm 的字段列表中使用方法。我没有尝试过,但您可以编写一个自定义小部件并将其设置为 ModelForm 中照片字段的默认小部件。
  • 也许用@property 在你的模型中装饰image_tag 方法会对你有所帮助。

标签: django django-models django-forms django-uploads


【解决方案1】:

正如Selcuk 提到的,最简单的解决方案是创建我自己的小部件。

from string import Template
from django.utils.safestring import mark_safe
from django.forms import ImageField

class PictureWidget(forms.widgets.Widget):
    def render(self, name, value, attrs=None, **kwargs):
        html =  Template("""<img src="$link"/>""")
        return mark_safe(html.substitute(link=value))

class UserProfileForm(forms.ModelForm):
    photo = ImageField(widget=PictureWidget)

【讨论】:

  • 错误:“模板”对象没有属性“替代”
  • 我的语法无效。你能澄清一下需要导入什么等
  • 非常感谢,至少现在它没有抱怨但没有图像出现那是因为我使用的是 S3。如何显示来自 photo.url 的图像?
【解决方案2】:

我还不能发表评论,因此我需要通过答案添加。这个方便的解决方案的所有功劳归功于 deathangel908。

我正在使用 Django 2.1,在尝试接受的答案时遇到TypeError: render() got an unexpected keyword argument 'renderer' 错误。这个问题的解决方法可以在这里找到:https://stackoverflow.com/a/52039655/8002464

简而言之,对于 Django 2.1 及更高版本,该解决方案必须稍作更改,并包括 renderer=None。完全更新的解决方案:

from string import Template
from django.utils.safestring import mark_safe
from django.forms ImageField

class PictureWidget(forms.widgets.Widget):
    def render(self, name, value, attrs=None, renderer=None):
        html =  Template("""<img src="$link"/>""")
        return mark_safe(html.substitute(link=value))

class UserProfileForm(forms.ModelForm):
    photo = ImageField(widget=PictureWidget)

顺便说一句。一个快速的小改进,至少对于我的用例来说是添加媒体 url。通过这种方式,图像实际上会显示出来,而不是丢失在错误的网址中。但这可能只是因为我如何设置我的网址。

from django.conf import settings

[...]
    html = Template("""<img src="$media$link"/>""")
    return mark_safe(html.substitute(media=settings.MEDIA_URL, link=value))

【讨论】:

    【解决方案3】:

    让我改进这个有趣的问题。我建议结合图像输入和图像预览。子类 FileInput 小部件并覆盖渲染以连接其他 html 以显示预览图像和默认输入图像 html。

    from django.utils.safestring import mark_safe
    from django import forms
    
    class ImagePreviewWidget(forms.widgets.FileInput):
        def render(self, name, value, attrs=None, **kwargs):
            input_html = super().render(name, value, attrs=None, **kwargs)
            img_html = mark_safe(f'<br><br><img src="{value.url}"/>')
            return f'{input_html}{img_html}'
    
    class UserProfileForm(forms.ModelForm):
    
        class Meta:
            model = UserProfile
            fields = '__all__'
    
        photo = forms.ImageField(widget=ImagePreviewWidget,)
    

    【讨论】:

    • 小心 xss f'{value.url}'。此代码在mark_safe 之前进行评估
    • @deathangel908 XSS 攻击包括在页面中注入 HTML(或 JS)代码。 value.url 取自数据库,是安全的
    • value.url 在经过消毒和验证之前是安全的。但如果不是,最好使用 string.Template 当然
    • value.url 看起来比 (media=settings.MEDIA_URL, link=value)
    • 您可以在文件名中加上双引号,因此如果您允许用户上传,这问题。在插入值之前,您应该在值上使用django.utils.html.escape
    【解决方案4】:
    from django.utils.safestring import mark_safe
    
    
    class ImagePreviewWidget(forms.widgets.FileInput):
        def render(self, name, value, attrs=None, **kwargs):
            input_html = super().render(name, value, attrs=None, **kwargs)
            if value:
                img_html = mark_safe(
                    f'<br><br><img src="{value.url}" width="200" />')
                return f'{input_html}{img_html}'
            return input_html
    

    如果您想使用相同的表单进行创建和更新,我建议您稍作改动。 if 条件将检查值是否存在“是更新表单”,否则只给你输入字段。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-04-24
      • 2015-01-08
      • 1970-01-01
      • 2020-05-23
      • 2015-12-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多