Search code examples
wagtailwagtail-adminwagtail-snippet

wagtail snippet index page does not show image itself, but show image name


My snippet has a image field. The image is not shown in snippet admin index page.


class Product(ClusterableModel):
    name = models.CharField(max_length=255)
    image = models.ForeignKey(
        "wagtailimages.Image",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="+",
    )
    sku = models.CharField(max_length=128)
    number = models.IntegerField()

    def schedule(self):
        return format_html("<a>First</a> | <a>Second</a>")

    panels = [
        FieldPanel("name"),
        FieldPanel("image"),
        FieldPanel("sku"),
        FieldPanel("number"),
    ]

    def __str__(self):
        return self.name

class ProductViewSet(SnippetViewSet):
    model = Product
    menu_icon = "pilcrow"  # change as required
    menu_order = 200  # will put in 3rd place (000 being 1st, 100 2nd)
    add_to_admin_menu = True  # or False to exclude your model from the menu

    search_fields = ("name", "sku")
    list_display = ("name", "sku", "image", "number", "schedule")

register_snippet(ProductViewSet)

it shows like this example index admin page

I want to show the image itself, not the image name.


Solution

  • You need to create a custom column type for your index view that handles displaying the image. You're seeing the name because it's the __str__() value of the Image object.

    I use the following on my sites:

    from wagtail.admin.ui.tables import TitleColumn
    from django.utils.safestring import mark_safe
    
    class ImageColumn(TitleColumn):
        def get_cell_context_data(self, instance, parent_context):
            context = super().get_cell_context_data(instance, parent_context)
            try:
                context['value'] = mark_safe(
                    context['value'].get_rendition('height-50').img_tag({'class':"show-transparency"})
                    )
            except:
                context['value'] = mark_safe(
                    '''
                    <svg class="icon icon-image" height="50px" 
                         viewBox="0 0 24 24" aria-hidden="true">
                        <use href="#icon-image"></use>
                    </svg>
                    '''
                    )
            return context
    

    It inherits Wagtail's TitleColumn - this allows it to be clickable if you use it in a custom chooser.

    It overrides the get_cell_context_data() method to return the HTML for the rendered image. I've set height-50 to scale the image down without cropping (my preference) - use fill50x50 if you want square thumbnails. It adds the show-transparency css class to make viewing images with transparent backgrounds easier.

    In the case that there is a problem with the rendition, or the image doesn't exist, the default image icon is rendered in place.

    To use in your list view, you can use something like:

    class ProductViewSet(SnippetViewSet):
        model = Product
        list_display = [
            "title", 
            "sku", 
            ImageColumn("image"), 
            UpdatedAtColumn()
        ]
    
    register_snippet(ProductViewSet)
    

    If you define your list display in the snippet viewset like this, you cannot set a custom column type in the first column. The view will be rendered without a link or action button set on each item. If you want to change that, you need to dive into creating a custom SnippetViewSet that calls a custom generic view and handles this for your own case.