Django Admin Показать изображение с Imagefield

Хотя я могу показать загруженное изображение в list_display, возможно ли это сделать на странице модели (как на странице, которую вы получаете для смены модели)?

Быстрый образец модели будет:

Class Model1(models.Model):
    image = models.ImageField(upload_to=directory)

Администратор по умолчанию показывает URL загруженного изображения, но не само изображение.

Спасибо!

14 ответов

Решение

Конечно. В своем классе модели добавьте метод, подобный следующему:

def image_tag(self):
    return u'<img src="%s" />' % <URL to the image>
image_tag.short_description = 'Image'
image_tag.allow_tags = True

и в вашем admin.py добавлять:

fields = ( 'image_tag', )
readonly_fields = ('image_tag',)

на ваш ModelAdmin, Если вы хотите ограничить возможность редактирования поля изображения, обязательно добавьте его в поле exclude приписывать.

Примечание: с Django 1.8 и 'image_tag' только в readonly_fields он не отображался. С 'image_tag' только в полях, он выдал ошибку неизвестного поля. Вам нужно это как в полях, так и в readonly_fields для корректного отображения.

В дополнение к ответу Майкла С. О'Коннора

Обратите внимание, что в Django v.1.9

image_tag.allow_tags = True

устарела, и вы должны использовать format_html(), format_html_join() или mark_safe()

Поэтому, если вы храните свои загруженные файлы в папке public / directory, ваш код должен выглядеть следующим образом:

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'

и в вашем admin.py добавить:

fields = ['image_tag']
readonly_fields = ['image_tag']

Это можно сделать в админке без изменения модели

from django.utils.html import format_html


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',]

admin.site.register(Model1, Model1Admin)

Для Django 1.9 Чтобы показать изображение вместо пути к файлу на страницах редактирования, использование ImageWidget - хороший способ сделать это.

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)

Использование:

class IndividualBirdAdmin(ImageWidgetAdmin):
    image_fields = ['thumbNail', 'detailImage']

Изображения будут отображаться для полей, thumbNail а также detailImage

С django-imagekit вы можете добавить любое изображение, как это:

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 или сгенерирована в классах администратора модели. Более семантическое решение:

Переопределение шаблона администратора

Примечание. Очевидно, моя репутация недостаточно высока, чтобы размещать более двух простых ссылок, поэтому я создал аннотации в следующем тексте и включил соответствующие URL-адреса в нижней части этого ответа.

Из документации сайта администратора Django:

Относительно легко переопределить многие шаблоны, которые использует модуль администратора для создания различных страниц сайта администратора. Вы даже можете переопределить некоторые из этих шаблонов для определенного приложения или конкретной модели.

Джанго django.contrib.admin.options.ModelAdmin (обычно доступный под пространством имен django.contrib.admin.ModelAdmin) представляет серию возможных путей к шаблону в загрузчике шаблонов Django в порядке от наиболее конкретного к менее. Этот фрагмент был скопирован непосредственно из 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)

Поэтому, учитывая вышеупомянутую документацию по переопределению шаблонов администратора Django и пути поиска шаблонов, предположим, что было создано приложение "Articles", в котором определен класс модели "Article". Если кто-то хочет переопределить или расширить только стандартную форму изменения сайта администратора Django для модели articles.models.Articleможно выполнить следующие шаги:

  1. Создайте структуру каталогов шаблонов для файла переопределения.
    • Хотя в документации это не упоминается, загрузчик шаблонов сначала будет искать в каталогах приложений, если APP_DIRS1 установлен в True,
    • Поскольку кто-то хочет переопределить шаблон сайта администратора Django по метке приложения и по модели, результирующая иерархия каталогов будет: <project_root>/articles/templates/admin/articles/article/
  2. Создайте файл (ы) шаблона в новой структуре каталогов.
    • Только форма изменения администратора должна быть переопределена, поэтому создайте change_form.html,
    • Окончательный, абсолютный путь будет <project_root>/articles/templates/admin/articles/article/change_form.html
  3. Полностью переопределить или просто расширить шаблон формы изменения администратора по умолчанию.
    • Я не смог найти какую-либо информацию в документации Django, касающуюся контекстных данных, доступных для стандартных шаблонов сайтов администратора, поэтому я был вынужден взглянуть на исходный код Django.
      • Шаблон формы изменений по умолчанию: github.com/django/django/blob/master/django/contrib/admin/templates/admin/change_form.html
      • Несколько соответствующих определений контекстного словаря можно найти в django.contrib.admin.options.ModelAdmin._changeform_view а также django.contrib.admin.options.ModelAdmin.render_change_form

Мое решение

Предполагая, что моим именем атрибута ImageField в модели является "файл", мой переопределение шаблона для реализации предварительного просмотра изображения будет примерно таким:

{% 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 представляется экземпляром модели, из которого была сгенерирована ModelForm. Кроме того, я обычно не использую встроенный CSS, но это не стоило отдельного файла для одного правила.

Источники:

  1. docs.djangoproject.com/en/dev/ref/settings/#app-dirs

Я пытался понять это сам и вот что я придумал

@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 2.1 без изменения models.py:

В вашем Hero модель, у вас есть поле изображения.

headshot = models.ImageField(null=True, blank=True, upload_to="hero_headshots/")

Вы можете сделать это так:

@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 v3.2.*

  • Просто вы можете этот код в вашем 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))
  • Затем добавьте это в admin.py
      list_display = ['image_tag']

Обновление Django 2.1 для ответа Venkat Kotra. Ответ отлично работает на Django 2.0.7 и ниже. Но выдает 500 ошибку сервера (если DEBUG=False) или дает

render() got an unexpected keyword argument 'renderer'

Причина в том, что в Django 2.1: Support for Widget.render() methods without the renderer argument is removed. Итак, парам renderer сейчас обязательно. Мы должны обновить функцию render() из AdminImageWidget включить парам renderer, И это должно быть после attrs (до kwargs если есть)

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

Например, ниже представлена ​​модель :

      # "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

И есть Productадмин ниже:

      # "admin.py"

from django.contrib import admin
from .models import Product

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
    pass

Затем загруженное изображение не отображается на странице «Изменить» в Django Admin, как показано ниже:

Теперь я переопределяюAdminFileWidgetзатем назначьтеCustomAdminFileWidgetв formfield_overrides , как показано ниже:

      # "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}
    }

Затем загруженное изображение отображается на странице «Изменить» в Django Admin, как показано ниже:

Вы также можете увидеть мой ответ , объясняющий, как отображать загруженные изображения на странице «Список изменений» в Django Admin.

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

Ответ @palamunder работал у меня на Django 2.2 с парой незначительных изменений.

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 + 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>
Другие вопросы по тегам