109👍
In addition to the answer of Michael C. O’Connor
Note that since Django v.1.9 (updated – tested and worked all the way to Django 3.0)
image_tag.allow_tags = True
is deprecated and you should use format_html(), format_html_join(), or mark_safe() instead
So if you are storing your uploaded files in your public /directory folder, your code should look like this:
from django.utils.html import mark_safe
Class Model1(models.Model):
image = models.ImageField(upload_to=directory)
def image_tag(self):
return mark_safe('<img src="/directory/%s" width="150" height="150" />' % (self.image))
image_tag.short_description = 'Image'
and in your admin.py add:
fields = ['image_tag']
readonly_fields = ['image_tag']
139👍
Sure. In your model class add a method like:
def image_tag(self):
from django.utils.html import escape
return u'<img src="%s" />' % escape(<URL to the image>)
image_tag.short_description = 'Image'
image_tag.allow_tags = True
and in your admin.py
add:
fields = ( 'image_tag', )
readonly_fields = ('image_tag',)
to your ModelAdmin
. If you want to restrict the ability to edit the image field, be sure to add it to the exclude
attribute.
Note: With Django 1.8 and ‘image_tag’ only in readonly_fields it did not display. With ‘image_tag’ only in fields, it gave an error of unknown field. You need it both in fields and in readonly_fields in order to display correctly.
- [Django]-Querying django migrations table
- [Django]-Django values_list vs values
- [Django]-Remove pk field from django serialized objects
68👍
It can be done in admin without modifying model
from django.utils.html import format_html
@admin.register(Model1)
class Model1Admin(admin.ModelAdmin):
def image_tag(self, obj):
return format_html('<img src="{}" />'.format(obj.image.url))
image_tag.short_description = 'Image'
list_display = ['image_tag',]
- [Django]-Django queries – id vs pk
- [Django]-AccessDenied when calling the CreateMultipartUpload operation in Django using django-storages and boto3
- [Django]-Unresolved attribute reference 'objects' for class '' in PyCharm
20👍
For Django 1.9
To show image instead of the file path in edit pages, using ImageWidget is nice way to do it.
from django.contrib.admin.widgets import AdminFileWidget
from django.utils.translation import ugettext as _
from django.utils.safestring import mark_safe
from django.contrib import admin
class AdminImageWidget(AdminFileWidget):
def render(self, name, value, attrs=None):
output = []
if value and getattr(value, "url", None):
image_url = value.url
file_name = str(value)
output.append(u' <a href="%s" target="_blank"><img src="%s" alt="%s" /></a> %s ' % \
(image_url, image_url, file_name, _('Change:')))
output.append(super(AdminFileWidget, self).render(name, value, attrs))
return mark_safe(u''.join(output))
class ImageWidgetAdmin(admin.ModelAdmin):
image_fields = []
def formfield_for_dbfield(self, db_field, **kwargs):
if db_field.name in self.image_fields:
request = kwargs.pop("request", None)
kwargs['widget'] = AdminImageWidget
return db_field.formfield(**kwargs)
return super(ImageWidgetAdmin, self).formfield_for_dbfield(db_field, **kwargs)
Usage:
class IndividualBirdAdmin(ImageWidgetAdmin):
image_fields = ['thumbNail', 'detailImage']
Images will show up for the fields, thumbNail
and detailImage
- [Django]-Django REST Framework custom fields validation
- [Django]-Django proxy model and ForeignKey
- [Django]-How do I remove Label text in Django generated form?
16👍
With django-imagekit you can add any image like this:
from imagekit.admin import AdminThumbnail
@register(Fancy)
class FancyAdmin(ModelAdmin):
list_display = ['name', 'image_display']
image_display = AdminThumbnail(image_field='image')
image_display.short_description = 'Image'
readonly_fields = ['image_display'] # this is for the change form
- [Django]-Django models avoid duplicates
- [Django]-Django dynamic model fields
- [Django]-Django: TemplateDoesNotExist (rest_framework/api.html)
11👍
While there are some good, functional solutions already shared here, I feel that non-form markup, such as auxiliary image tags, belong in templates, not tacked on to Django form widgets or generated in model admin classes. A more semantic solution is:
Admin Template Overrides
Note: Apparently my reputation isn’t high enough to post more than two simple links, so I have created annotations in the following text and included the respective URLs at the bottom of this answer.
From the Django Admin Site documentation:
It is relatively easy to override many of the templates which the admin module uses to generate the various pages of an admin site. You can even override a few of these templates for a specific app, or a specific model.
Django’s django.contrib.admin.options.ModelAdmin
(commonly accessed under the namespace django.contrib.admin.ModelAdmin
) presents a series of possible template paths to Django’s template loader in order from most specific to less so. This snippet was copied directly from django.contrib.admin.options.ModelAdmin.render_change_form
:
return TemplateResponse(request, form_template or [
"admin/%s/%s/change_form.html" % (app_label, opts.model_name),
"admin/%s/change_form.html" % app_label,
"admin/change_form.html"
], context)
Therefore, considering the aforementioned Django admin template override documentation and the template search paths, suppose one has created an app "articles" in which is defined a model class "Article". If one wants to override or extend only the default Django admin site change form for model articles.models.Article
, one would execute the following steps:
- Create a template directory structure for the override file.
- Although the documentation does not mention it, the template loader will look in app directories first if
APP_DIRS
1 is set toTrue
. - Because one wants to override the Django admin site template by app label and by model, the resulting directory hierarchy would be:
<project_root>/articles/templates/admin/articles/article/
- Although the documentation does not mention it, the template loader will look in app directories first if
- Create the template file(s) in one’s new directory structure.
- Only the admin change form needs to be overridden so create
change_form.html
. - The final, absolute path will be
<project_root>/articles/templates/admin/articles/article/change_form.html
- Only the admin change form needs to be overridden so create
- Completely override or simply extend the default admin change form template.
- I wasn’t able to locate any information in the Django documentation concerning the context data available to the default admin site templates so I was forced to look at the Django source code.
- Default change form template: github.com/django/django/blob/master/django/contrib/admin/templates/admin/change_form.html
- A few of the relevant context dictionary definitions can be found in
django.contrib.admin.options.ModelAdmin._changeform_view
anddjango.contrib.admin.options.ModelAdmin.render_change_form
- I wasn’t able to locate any information in the Django documentation concerning the context data available to the default admin site templates so I was forced to look at the Django source code.
My Solution
Assuming that my ImageField attribute name on the model is "file", my template override to implement image previews would be similar to this:
{% extends "admin/change_form.html" %}
{% block field_sets %}
{% if original %}
<div class="aligned form-row">
<div>
<label>Preview:</label>
<img
alt="image preview"
src="/{{ original.file.url }}"
style="max-height: 300px;">
</div>
</div>
{% endif %}
{% for fieldset in adminform %}
{% include "admin/includes/fieldset.html" %}
{% endfor %}
{% endblock %}
original
appears to be the model instance from which the ModelForm was generated. As an aside, I usually don’t use inline CSS but it wasn’t worth a separate file for a single rule.
Sources:
- docs.djangoproject.com/en/dev/ref/settings/#app-dirs
- [Django]-ImportError: Couldn't import Django
- [Django]-Django: Use of DATE_FORMAT, DATETIME_FORMAT, TIME_FORMAT in settings.py?
- [Django]-Checking for empty queryset in Django
8👍
I was trying to figure it out myself and this is what i came up with
@admin.register(ToDo)
class ToDoAdmin(admin.ModelAdmin):
def image_tag(self, obj):
return format_html('<img src="{}" width="auto" height="200px" />'.format(obj.img.url))
image_tag.short_description = 'Image'
list_display = ['image_tag']
readonly_fields = ['image_tag']
- [Django]-Django.db.utils.ProgrammingError: relation "bot_trade" does not exist
- [Django]-How do I use django rest framework to send a file in response?
- [Django]-How to repeat a "block" in a django template
5👍
This is how it worked for django 2.1 without modifying models.py
:
In your Hero
model, you have an image field.:
headshot = models.ImageField(null=True, blank=True, upload_to="hero_headshots/")
You can do it like this:
@admin.register(Hero)
class HeroAdmin(admin.ModelAdmin, ExportCsvMixin):
readonly_fields = [..., "headshot_image"]
def headshot_image(self, obj):
return mark_safe('<img src="{url}" width="{width}" height={height} />'.format(
url = obj.headshot.url,
width=obj.headshot.width,
height=obj.headshot.height,
)
)
- [Django]-Favorite Django Tips & Features?
- [Django]-Adding to the "constructor" of a django model
- [Django]-How do I get the object if it exists, or None if it does not exist in Django?
4👍
Tested on Django v3.2.*
- Just you can write this code in your
model.py
from django.db import models
from django.utils.html import mark_safe
class Book(models.Model):
image = models.ImageField()
def image_tag(self):
if self.image != '':
return mark_safe('<img src="%s%s" width="150" height="150" />' % (f'{settings.MEDIA_URL}', self.image))
- Then add this in
admin.py
list_display = ['image_tag']
- [Django]-Why is logged_out.html not overriding in django registration?
- [Django]-Django urls without a trailing slash do not redirect
- [Django]-__init__() got an unexpected keyword argument 'mimetype'
3👍
Django 2.1 update for Venkat Kotra’s answer. The answer works fine on Django 2.0.7 and below. But gives server 500 error (if DEBUG=False
) or gives
render() got an unexpected keyword argument 'renderer'
The reason is that in Django 2.1: Support for Widget.render() methods without the renderer argument is removed.
So, param renderer
is mandatory now. We must update function render()
of AdminImageWidget
to include param renderer
. And it must be after attrs
(before kwargs
if you have it):
class AdminImageWidget(AdminFileWidget):
def render(self, name, value, attrs=None, renderer=None):
output = []
if value and getattr(value, "url", None):
image_url = value.url
file_name = str(value)
output.append(u' <a href="%s" target="_blank"><img src="%s" alt="%s" /></a> %s ' % \
(image_url, image_url, file_name, _('Change:')))
output.append(super(AdminFileWidget, self).render(name, value, attrs, renderer))
return mark_safe(u''.join(output))
- [Django]-How to send email via Django?
- [Django]-How do Django models work?
- [Django]-Django Queryset with year(date) = '2010'
3👍
Django ver. 3.0.3
models.py:
def image_tag(self):
from django.utils.html import mark_safe
return mark_safe('<img src="%s" width="100px" height="100px" />'%(self.image.url))
image_tag.short_description = 'Image'
admin.py:
list_display = ('image_tag', )
- [Django]-Authenticate by IP address in Django
- [Django]-Why won't Django use IPython?
- [Django]-How to get the ID of a just created record in Django?
2👍
@palamunder’s answer worked for me on Django 2.2 with a couple minor changes.
Model.py
from django.utils.safestring import mark_safe
class AdminCategory(models.Model):
image = models.ImageField(_("Image"),
upload_to='categories/',
blank=True,
default='placeholder.png')
def image_tag(self):
return mark_safe('<img src="%s" width="150" height="150" />' % (
self.image.url)) # Get Image url
image_tag.short_description = 'Image'
Admin.py
admin.site.register(
AdminCategory,
list_display=["image_tag"],
)
- [Django]-Django Rest Framework – Could not resolve URL for hyperlinked relationship using view name "user-detail"
- [Django]-Pytest.mark.parametrize with django.test.SimpleTestCase
- [Django]-How to add a cancel button to DeleteView in django
2👍
For example, there is Product
model below:
# "models.py"
from django.db import models
class Product(models.Model):
name = models.CharField(max_length=50)
price = models.DecimalField(decimal_places=2, max_digits=5)
image = models.ImageField()
def __str__(self):
return self.name
And, there is Product
admin below:
# "admin.py"
from django.contrib import admin
from .models import Product
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
pass
Then, an uploaded image is not displayed in "Change" page in Django Admin as shown below:
Now, I override AdminFileWidget
then assign CustomAdminFileWidget
to formfield_overrides as shown below:
# "admin.py"
from django.contrib import admin
from .models import Product
from django.contrib.admin.widgets import AdminFileWidget
from django.utils.html import format_html
from django.db import models
# Here
class CustomAdminFileWidget(AdminFileWidget):
def render(self, name, value, attrs=None, renderer=None):
result = []
if hasattr(value, "url"):
result.append(
f'''<a href="{value.url}" target="_blank">
<img
src="{value.url}" alt="{value}"
width="100" height="100"
style="object-fit: cover;"
/>
</a>'''
)
result.append(super().render(name, value, attrs, renderer))
return format_html("".join(result))
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
formfield_overrides = { # Here
models.ImageField: {"widget": CustomAdminFileWidget}
}
Then, an uploaded image is displayed in "Change" page in Django Admin as shown below:
You can also see my answer explaining how to display uploaded images in "Change List" page in Django Admin.
- [Django]-Django: remove a filter condition from a queryset
- [Django]-What is choice_set in this Django app tutorial?
- [Django]-Django limit_choices_to for multiple fields with "or" condition
0👍
If you need to show image preview before save, you could use custom django template + js
admin.py
class UploadedImagePreview(object):
short_description = _('Thumbnail')
allow_tags = True
def __init__(self, image_field, template, short_description=None, width=None, height=None):
self.image_field = image_field
self.template = template
if short_description:
self.short_description = short_description
self.width = width or 200
self.height = height or 200
def __call__(self, obj):
try:
image = getattr(obj, self.image_field)
except AttributeError:
raise Exception('The property %s is not defined on %s.' %
(self.image_field, obj.__class__.__name__))
template = self.template
return render_to_string(template, {
'width': self.width,
'height': self.height,
'watch_field_id': 'id_' + self.image_field # id_<field_name> is default ID
# for ImageField input named `<field_name>` (in Django Admin)
})
@admin.register(MyModel)
class MainPageBannerAdmin(ModelAdmin):
image_preview = UploadedImagePreview(image_field='image', template='admin/image_preview.html',
short_description='uploaded image', width=245, height=245)
readonly_fields = ('image_preview',)
fields = (('image', 'image_preview'), 'title')
image_preview.html
<img id="preview_{{ watch_field_id }}" style="display: none; width: {{ width }}px; height: {{ height }}px" alt="">
<script>
function handleFileSelect(event) {
var files = event.target.files; // FileList object
// Loop through the FileList and render image files as thumbnails
for (var i = 0, f; f = files[i]; i++) {
// Only process image files
if (!f.type.match('image.*')) continue;
// Init FileReader()
// See: https://developer.mozilla.org/en-US/docs/Web/API/FileReader
var reader = new FileReader();
// Closure to capture the file information
reader.onload = (function () {
return function (e) {
// Render background image
document.getElementById('preview_{{watch_field_id}}').src = e.target.result;
// Set `display: block` to preview image container
document.getElementById('preview_{{watch_field_id}}').style.display = 'block';
};
})(f);
// Read in the image file as a data URL
reader.readAsDataURL(f);
}
}
// Change img src after change file input
// watch_field_id — is ID for ImageField input
document.getElementById('{{ watch_field_id }}').addEventListener('change', handleFileSelect, false);
</script>
- [Django]-Django count RawQuerySet
- [Django]-Django migration error :you cannot alter to or from M2M fields, or add or remove through= on M2M fields
- [Django]-Is this the right way to do dependency injection in Django?