Display image from ImageField by means of form

2020-03-24 05:33发布

prelude:

Here's the simpliest way to display an ImageField. Lets assume I have 10 fields in my form and I don't wanna iterate over all of them in template.html just to check if it's an imageField and display it in a different way. I want to handle it via form or widget or something like this. So I want the leave template.html as it's bellow.

template.html

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

And add to models.py an image_tag method:

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)

I get an error that Unknown field(s) (image_tag) specified for UserProfile which is fair enought.

I also tried to place it in readonly_fields but they don't display somehow. Do I have to create list_view? If yes where should it be?

What do I miss here? Why the image_tag method should be a part of model instead of form? Model describes how data should persists in the database, not how it should present to user via form. This should be a part of form, shouldn't it?. Correct me please if I'm wrong. How do I solve the issue? Any help would be greatly appreciated!

3条回答
女痞
2楼-- · 2020-03-24 05:45

As Selcuk mentioned the simplest solution was to create my own widget.

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)
查看更多
贪生不怕死
3楼-- · 2020-03-24 05:53

Let me improve this interesting question. I suggest to combine image input and image preview. Subclass FileInput widget and override render to concatenate additional html to display preview image and default input image 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,)
查看更多
趁早两清
4楼-- · 2020-03-24 06:09

I can't comment yet, thus I need to add this via an Answer. All credits to deathangel908 for this handy solution.

I'm using Django 2.1 and was struck with TypeError: render() got an unexpected keyword argument 'renderer' error when trying the accepted answer. Solution to this problem can be found here: https://stackoverflow.com/a/52039655/8002464

In short, the solution must be slightly changed for Django 2.1 and upwards and include renderer=None. Fully updated solution:

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)

Btw. a quick small improvement, at least for my use-case was to add the media url. This way the images were actually shown instead of being lost in wrong urls. But this might only be because of how I set up my urls.

from django.conf import settings

[...]
    html = Template("""<img src="$media$link"/>""")
    return mark_safe(html.substitute(media=settings.MEDIA_URL, link=value))
查看更多
登录 后发表回答