Отображение загруженных изображений в HTML-документе и админ-панели

Курс по Django: https://stepik.org/a/183363

Архив проекта: 50_sitewomen.zip

На данный момент мы с вами умеем загружать произвольные файлы на сервер и сделали загрузку изображений для наших постов. Давайте теперь настроим отображение изображения при просмотре поста. Но сначала посмотрим, что из себя представляет поле photo.

Перейдем в консоль фреймворка Django:

python manage.py shell_plus

И прочитаем из таблицы women последнюю запись:

w = Women.objects.all()[0]

У объекта w имеется атрибут photo:

w.photo

который ссылается на объект ImageFieldFile. Если после photo поставить точку и нажать Tab, то увидим множество атрибутов и методов этого объекта. В частности, нас будет интересовать локальный атрибут url:

w.photo.url

который возвращает URL-адрес для получения изображения с сервера. Давайте им воспользуемся и пропишем в шаблоне post.html следующие строчки сразу после заголовка первого уровня:

{% if post.photo %}
<img class="img-article-left" src="{{post.photo.url}}"></p>
{% endif %}

Однако если сейчас открыть последний пост, то увидим, что изображение для него не загружается. И, действительно, открыв вкладу «Network» в браузере и обновив страницу, видим статус 404 у изображения. Дело в том, что пока сервер не может связать этот URL с физическим маршрутом к файлу изображения на сервере. Для этого нам нужно сделать следующие действия. В файле конфигурации settings.py пропишем еще один параметр:

MEDIA_URL = '/media/'

На что он влияет? Если сейчас обновить страницу поста, то URL-маршрут к изображению будет иметь этот префикс media. Зачем он нужен? Смотрите, благодаря единому уникальному префиксу для всех подгружаемых медиа-файлов, мы можем в urls.py пакета конфигурации определить для них маршрут выдачи этих файлов. Для этого пропишем такие строчки:

if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

То есть, мы говорим, если веб-сервер работает в режиме отладки, то в список маршрутов нужно добавить еще один с префиксом media и связать его с каталогом MEDIA_ROOT, где эти файлы расположены. Только так в режиме отладки мы можем раздавать загруженные файлы. В боевом режиме в этом нет необходимости, т.к. сервер уже будет иметь необходимые настройки для связки маршрута с рабочим каталогом.

Обновляем страницу и видим, что теперь все работает корректно.

Давайте по аналогии сделаем отображение изображений в списке постов. Для этого перейдем в шаблон index.html и перед заголовком h2 пропишем следующие строчки:

{% if p.photo %}
                   <p ><img class="img-article-left thumb" src="{{p.photo.url}}"></p>
{% endif %}

Переходим на главную страницу, видим изображение в списке постов.

Отображение изображений в админ-панели

Следующим шагом добавим отображение миниатюр изображений, связанных с нашими постами, непосредственно в списке. Перейдем в файл women/admin.py и в классе WomenAdmin в списках fields и list_display укажем поле photo:

fields = ['title', 'slug', 'content', 'photo', 'cat', 'husband', 'tags']
list_display = ('title', 'photo', 'time_create', 'is_published', 'cat')

А поле brief_info я удалю.

Переходим в админ-панель:

http://127.0.0.1:8000/admin/

и в списке женщин в поле photo отображается путь к изображению. Но мы бы хотели здесь видеть именно изображение, а не путь к нему. Для этого сформируем новое вычисляемое поле, по аналогии с полем brief_info. Переименуем метод brief_info в post_photo и возвратим строку в виде тега img:

    @admin.display(description="Изображение")
    def post_photo(self, women: Women):
        return mark_safe(f"<img src='{women.photo.url}' width=50>")

Здесь функция mark_safe() используется для того, чтобы теги не экранировались и отрабатывали как HTML-теги. Осталось в список list_display вместо photo указать post_photo:

list_display = ('title', 'post_photo', 'time_create', 'is_published', 'cat')

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

    @admin.display(description="Изображение")
    def post_photo(self, women: Women):
        if women.photo:
            return mark_safe(f"<img src='{women.photo.url}' width=50>")
        return "Без фото"

Теперь никаких ошибок нет, все работает как требуется.

Мало того, мы прямо здесь в админ-панели можем теперь редактировать посты и назначать им изображения. Все будет работать. Также можно сделать отображение миниатюры непосредственно при изменении статьи. Для этого достаточно указать ссылку на наш метод post_photo() в атрибутах:

@admin.register(Women)
class WomenAdmin(admin.ModelAdmin):
    fields = ['title', 'slug', 'content', 'photo', 'post_photo', 'cat', 'husband', 'tags']
    readonly_fields = ['post_photo']
    ...

Все, теперь мы видим изображение загруженной фотографии в форме редактирования.

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

save_on_top = True

добавляет верхнюю панель для управления записью, что бывает очень удобно.

Курс по Django: https://stepik.org/a/183363

Видео по теме