Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Display image from ImageField by means of form

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!

like image 600
deathangel908 Avatar asked Feb 27 '15 11:02

deathangel908


3 Answers

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)
like image 91
deathangel908 Avatar answered Sep 30 '22 17:09

deathangel908


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))
like image 40
Bloodmallet Avatar answered Sep 30 '22 16:09

Bloodmallet


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,)
like image 31
Valery Ramusik Avatar answered Sep 30 '22 17:09

Valery Ramusik