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
можно выполнить следующие шаги:
- Создайте структуру каталогов шаблонов для файла переопределения.
- Хотя в документации это не упоминается, загрузчик шаблонов сначала будет искать в каталогах приложений, если
APP_DIRS
1 установлен вTrue
, - Поскольку кто-то хочет переопределить шаблон сайта администратора Django по метке приложения и по модели, результирующая иерархия каталогов будет:
<project_root>/articles/templates/admin/articles/article/
- Хотя в документации это не упоминается, загрузчик шаблонов сначала будет искать в каталогах приложений, если
- Создайте файл (ы) шаблона в новой структуре каталогов.
- Только форма изменения администратора должна быть переопределена, поэтому создайте
change_form.html
, - Окончательный, абсолютный путь будет
<project_root>/articles/templates/admin/articles/article/change_form.html
- Только форма изменения администратора должна быть переопределена, поэтому создайте
- Полностью переопределить или просто расширить шаблон формы изменения администратора по умолчанию.
- Я не смог найти какую-либо информацию в документации 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
- Я не смог найти какую-либо информацию в документации Django, касающуюся контекстных данных, доступных для стандартных шаблонов сайтов администратора, поэтому я был вынужден взглянуть на исходный код Django.
Мое решение
Предполагая, что моим именем атрибута 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, но это не стоило отдельного файла для одного правила.
Источники:
- 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>