Когда мы научились загружать файлы в Django с помощью форм и моделей, следующий логичный шаг — научиться правильно отображать эти файлы. На этом уроке мы сосредоточимся именно на работе с загруженными изображениями: покажем их в шаблонах, выведем миниатюры в админ-панели и разберём важные настройки проекта, без которых изображения просто не будут доступны пользователю.
Когда вы загружаете изображение в Django через ImageField, оно сохраняется в папку, указанную параметром upload_to. Например:
poster = models.ImageField(upload_to="posters/%Y/%m/%d/")Django отлично сохраняет файл… но не раздаёт его пользователям автоматически. Это принципиальная позиция: Django ― это веб-фреймворк, а не сервер файлов.
Значит, нам нужно:
- Подготовить модель и данные.
- Настроить отдачу MEDIA-файлов.
- Научиться выводить изображения в HTML.
- Научиться отображать миниатюры изображений в админ-панели.
Этим мы и займёмся.
Для примеров возьмём модель фильма Movie (файл cinemahub/models.py):
from django.db import models
class Movie(models.Model):
title = models.CharField(max_length=255)
description = models.TextField(blank=True)
poster = models.ImageField(
upload_to="posters/%Y/%m/%d/",
blank=True,
null=True,
verbose_name="Постер"
)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.titleПоле poster — это наше изображение.
Если вы еще не сделали миграции:
python manage.py makemigrations
python manage.py migrateВ файле settings.py добавьте:
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'Здесь:
MEDIA_URL— URL-префикс, по которому будет загружаться изображение (например:/media/posters/2025/01/15/file.jpg)MEDIA_ROOT— папка, где Django сохраняет изображения.
Теперь добавим раздачу медиа-файлов в urls.py (файл проекта, не приложения):
# config/urls.py — главный urls.py проекта
from django.conf import settings
from django.conf.urls.static import static
from django.urls import path, include
urlpatterns = [
path('', include('cinemahub.urls')),
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)Важно: без этого шага никакие изображения не будут доступны браузеру.
Запустите сервер:
python manage.py runserverЗагрузите в админке постер к любому фильму.
Теперь в shell можно посмотреть содержимое:
python manage.py shellfrom cinemahub.models import Movie
m = Movie.objects.first()
print(m.poster) # путь внутри MEDIA_ROOT
print(m.poster.url) # URL вида "/media/posters/2025/...jpg"Если .url принимает значение — значит Django понимает путь к файлу.
Допустим, у нас есть страница просмотра фильма:
cinemahub/templates/cinemahub/movie_detail.html.
Хотим вывести изображение, но только если оно есть:
{% if movie.poster %}
<img src="{{ movie.poster.url }}" alt="Постер фильма" class="movie-poster" />
{% endif %}Проверка {% if movie.poster %} обязательна — если изображения нет, Django выдаст ошибку.
- Переходим в браузер на страницу фильма.
- Открываем инструменты разработчика → вкладка Network.
- Если изображение не грузится — ищем ошибку 404 по адресу
/media/....
Возможные ошибки и решения:
| Ошибка | Причина | Решение |
|---|---|---|
| 404 Not Found | Django не раздаёт MEDIA файлы | Проверьте настройки MEDIA_URL и static() в urls.py |
| net::ERR_FILE_NOT_FOUND | Папка media не существует |
Создайте её в проекте |
| Изображение не отображается | В шаблоне не добавлен .url |
Использовать {{ movie.poster.url }} |
В шаблоне списка фильмов cinemahub/movie_list.html:
{% for m in movies %}
<div class="movie-item">
{% if m.poster %}
<img src="{{ m.poster.url }}" alt="Постер" class="thumb" />
{% endif %}
<h3>{{ m.title }}</h3>
</div>
{% endfor %}По умолчанию админ-панель не показывает изображения — только текстовые пути.
Мы добавим миниатюры.
Откройте файл cinemahub/admin.py:
from django.contrib import admin
from django.utils.safestring import mark_safe
from .models import MovieДобавим метод отображения:
@admin.display(description="Постер")
def movie_poster(self, movie: Movie):
if movie.poster:
return mark_safe(f"<img src='{movie.poster.url}' width='60' />")
return "—"Теперь подключим в админ-класс:
@admin.register(Movie)
class MovieAdmin(admin.ModelAdmin):
list_display = ("title", "movie_poster", "created_at")
readonly_fields = ("movie_poster",)
fields = ("title", "description", "poster", "movie_poster")- Заходим в админ-панель.
- Открываем раздел фильмов.
- Видим маленькие миниатюры в списке.
- Заходим в карточку фильма — mini-preview там тоже отображается.
Если миниатюра не отображается:
- проверьте
MEDIA_URL - проверьте, что файл действительно лежит в
media/... - убедитесь, что
movie.poster.urlсуществует
cinemahub/
models.py
admin.py
views.py
forms.py
templates/cinemahub/
movie_list.html
movie_detail.html
media/
posters/...
config/
settings.py
urls.py
- Вывести изображение только если оно больше 0 байт. Создайте условие в шаблоне
movie_detail.html: не выводить изображение, если файл пустой.
- Добавить рамку к изображению в списке фильмов. Создайте CSS-класс
poster-frameи примените его к изображениям, но только если они есть.
- Добавить в админку отображение размера файла. Добавьте в
MovieAdminотдельное поле “Размер файла”. Пример вывода:"150 KB".
- Вывести изображение только если оно больше 0 байт
{% if movie.poster and movie.poster.size > 0 %}
<img src="{{ movie.poster.url }}" alt="Постер" />
{% endif %}- Добавить рамку к изображению в списке фильмов
HTML:
{% if m.poster %}
<img src="{{ m.poster.url }}" class="poster-frame" />
{% endif %}CSS:
.poster-frame {
border: 2px solid #ccc;
padding: 4px;
}- Добавить в админку отображение размера файла.
admin.py:
@admin.display(description="Размер файла")
def poster_size(self, movie: Movie):
if movie.poster:
size = movie.poster.size
kb = round(size / 1024)
return f"{kb} KB"
return "—"
@admin.register(Movie)
class MovieAdmin(admin.ModelAdmin):
list_display = ("title", "movie_poster", "poster_size", "created_at")- Почему изображения не отображаются в Django без настройки MEDIA?
- Что делает параметр
upload_toвImageField? - Почему важно использовать
<img src="{{ movie.poster.url }}">, а не просто{{ movie.poster }}? - Зачем добавлять проверку
{% if movie.poster %}? - Как работает
static()вurls.py? - Что делает функция
mark_safe()и почему она нужна в admin? - Как вывести миниатюру изображения в admin-панели?
- Где хранятся загруженные изображения в Django?
- Почему в продакшене нельзя раздавать MEDIA-файлы через Django?
- Что происходит, если удалить файл через файловую систему, но оставить запись в БД?