How to display inline uploaded images in "Change" page in Django Admin?

7.6k Views Asked by At

I'm trying to display inline uploaded images in "Change List" page in Django Admin:

enter image description here

This is my code 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)
    
    def __str__(self):
        return self.name

class ProductImage(models.Model):
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    image = models.ImageField()

    def __str__(self):
        return self.image.url
# "admin.py"

from django.contrib import admin
from .models import Product

class ProductImageInline(admin.TabularInline):
    model = ProductImage

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    inlines = (ProductImageInline,)

So, how can I display inline uploaded images in "Change" page in Django Admin?

6

There are 6 best solutions below

1
On

Cleaned up version of the first answer https://stackoverflow.com/a/44051507/2020723 - ready of Django >3, python 3.x:

class AdminImageWidget(AdminFileWidget):
    """Admin widget for showing clickable thumbnail of Image file fields"""

    def render(self, name, value, attrs=None, renderer=None):
        html = super().render(name, value, attrs, renderer)
        if value and getattr(value, 'url', None):
            html = format_html('<a href="{0}" target="_blank"><img src="{0}" alt="{1}" width="150" height="150" style="object-fit: contain;"/></a>', value.url, str(value)) + html
        return html
0
On

You need to override AdminFileWidget then assign CustomAdminFileWidget to formfield_overrides as shown below:

# "admin.py"

from django.contrib import admin
from .models import Product, ProductImage
from django.contrib.admin.widgets import AdminFileWidget
from django.utils.html import format_html
from django.db import models

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))

class ProductImageInline(admin.TabularInline):
    model = ProductImage
    formfield_overrides = {
        models.ImageField: {'widget': CustomAdminFileWidget}
    }

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    inlines = (ProductImageInline,)

Then, you can display inline uploaded images as shown below:

enter image description here

1
On

You'll want to add a method to the model.py:

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

thumbnail.short_description = 'Thumbnail'

Then in admin.py you'll want to add:

fields = ( 'thumbnail', )
readonly_fields = ('thumbail',)

That will add the thumbnail there.

1
On

I managed to do it after a lot of digging. Unfortunately I couldn't find the source I used (too many tabs, too many didn't work..) . So the idea was to create an Image Widget, that appends some html script.

from django.contrib.admin.widgets import AdminFileWidget

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" width="150" height="150"  style="object-fit: cover;"/></a> %s ' % \
                (image_url, image_url, file_name, _('')))
        output.append(super(AdminFileWidget, self).render(name, value, attrs))
        return mark_safe(u''.join(output))


class CollectionImageInline(admin.TabularInline):
    formfield_overrides = {models.ImageField: {'widget': AdminImageWidget}}

the result : (I changed admin theme to default regardless to this issue) enter image description here

0
On

Following on, to @shaniqwa answer, django(2.2.7) now requires renderer in render(),

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(
                f' <a href="{image_url}" target="_blank">'
                f'  <img src="{image_url}" alt="{file_name}" width="150" height="150" '
                f'style="object-fit: cover;"/> </a>')

        output.append(super(AdminFileWidget, self).render(name, value, attrs, renderer))
        return mark_safe(u''.join(output))


class ProductImagesAdmin(admin.TabularInline):
    model = ProductImages
    extra = 1
    formfield_overrides = {
        models.ImageField: {'widget': AdminImageWidget}
    }

1
On

The simple solution that works for me in Django 3.0:

In models.py add:

from django.utils.safestring import mark_safe

def image_preview(self):
    if self.image:
        return mark_safe('<img src="{0}" width="150" height="150" />'.format(self.image.url))
    else:
        return '(No image)'

in admin.py:

class ProductImagesAdmin(admin.TabularInline):
    model = ProductImages
    readonly_fields = ('image_preview',)