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!
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)
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))
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,)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With