Превью для ImageField в админке
Этот туториал поможет вам прокачать админку сайта с блогом. У каждого поста есть картинка, но в админке отображается только её название, а хочется увидеть, как она выглядит. Вы прикрутите к админке превью картинок, выглядеть будет примерно так:
Перед туториалом
В этом туториале подразумевается, что вы уже умеете работать с админкой джанги, а чтение моделей не вызывает у вас проблем.
Ссылки на материалы для подготовки:
1. Запустите сайт
Разверните у себя этот блог, чтобы продолжить.
Это блог создан в учебных целях для урока, где мы учим оптимизации Django, поэтому он немного странный и медленный.
Первым делом запустите проект, следуя инструкциям в README. Если всё сделано правильно, то по адресу http://127.0.0.1:8000/ вы увидите работающий сайт:
Сайт пока работает с пустой базой данных и выглядит совсем пустым. Исправьте это. Создайте суперпользователя, чтобы Django пустила вас в админку. Используйте команду createsuperuser:
$ python3 manage.py createsuperuser --username root
После зайдите в админку по адресу http://127.0.0.1:8000/ и добавьте две записи: один тег и один пост:
2. Добавьте посту новое поле в админке
По умолчанию админка показывает все поля модели данных. Но при желании это можно изменить: в админке можно добавить новые поля и надписи, которых не было у модели.
Для того, чтобы в админке появилось превью картинок поста, как раз пригодится такой приём: превью будет дополнительной формой в админке.
Новое поле можно создать в ModelAdmin простым добавлением нового метода:
class PostAdmin(admin.ModelAdmin):
readonly_fields = ["preview"]
def preview(self, obj):
return "Hello!"
admin.site.register(Post, PostAdmin)
Новый метод обязательно нужно добавить в readonly_fields
, чтобы он отобразился в админке. Вот что у нас получилось:
3. Добавьте картинку
Теперь, вместо бесполезного текста можно поставить картинку. Пока что любую из интернета, а потом уже заменим её на картинку, что залита в ImageField
:
class PostAdmin(admin.ModelAdmin):
readonly_fields = ["preview"]
def preview(self, obj):
return '<img src="https://dvmn.org/filer/canonical/1591892373/669/">'
Получился неожиданный результат, вместо HTML-тега с картинкой вставился этот же тег, но текстом:
Оказывается, админка не хочет добавлять абы что. В ней есть защита от таких вот HTML-включений и она заранее экранировала все символы так, чтобы теги преобразовались в текст.
Чтобы всё же добавить именно тег, нужно явно сказать админке, что строка с тегами безопасная. Это умеет делать метод mark_safe():
from django.utils.safestring import mark_safe
class PostAdmin(admin.ModelAdmin):
readonly_fields = ["preview"]
def preview(self, obj):
return mark_safe("<img src='https://dvmn.org/filer/canonical/1591892373/669/'>")
Теперь картинка добавилась как надо:
4. Замените статичную картинку на картинку из поля
От превью не будет никакого толку, если оно будет показывать какую-то статичную картинку. Превью должно показывать картинку из ImageField
этой модели. Но как же до неё добраться?
Метод preview
получает два аргумента: self
и obj
. Первый — это сам класс PostAdmin
, а obj
— это объект, на который вы смотрите в админке. То есть это и есть пост. Теперь можно достать из него картинку, а у неё получить url
:
from django.utils.safestring import mark_safe
class PostAdmin(admin.ModelAdmin):
readonly_fields = ["preview"]
def preview(self, obj):
return mark_safe(f'<img src="{obj.image.url}">')
Получилось превью, но радоваться победе пока рано, есть ещё несколько вещей, которые стоит сделать:
5. Перенесите превью к полю с картинкой
Порядок полей в админке тоже можно контролировать. По умолчанию все поля располагаются в таком порядке, в каком они объявлены в models.py
. Но его можно поменять: достаточно определить параметр fields
и перчислить поля модели в том порядке, в котором вы хотите. Мы расположили так:
class PostAdmin(admin.ModelAdmin):
fields = ['title', 'slug', 'author', 'image', 'preview',
'text', 'tags', 'published_at', 'likes']
readonly_fields = ["preview"]
def preview(self, obj):
return mark_safe(f'<img src="{obj.image.url}">')
Как это выглядит:
6. Измените размер превью
Размер картинки можно менять через стили тега <img>
:
def preview(self, obj):
return mark_safe(f'<img src="{obj.image.url}" style="max-height: 200px;">')
В данном примере мы ограничили только высоту картинки до 200 пикселей. Благодаря этому она сама перемасштабируется так, чтобы высота не превышала 200 пикселей, но она не сжалась ни горизонтально, ни вертикально:
Что делать, если ошибка
В процессе написания туториала у нас сломался с виду рабочий код:
from django.utils.safestring import mark_safe
class PostAdmin(admin.ModelAdmin):
readonly_fields = ["preview"]
def preview(self, obj):
return mark_safe('<img src="{obj.image.url}">')
Вместо превью отрисовалась поломанная картинка:
Вместо разглядывания кода можно открыть Chrome Dev Tools и посмотреть что сломалось:
Оказалось, что в f-строке не хватало буквы f
:
'<img src="{obj.image.url}">'
vs.
f'<img src="{obj.image.url}">'