Добавление картинок к постам
Django — это огромная экосистема из всевозможных модулей, расширяющих возможности базового фреймворка. На сайте
PyPI.org размещены десятки тысяч расширений, в названии которых есть слово
django, а неопубликованных модулей или тех, что названы как-то иначе — ещё больше.
Пустим в дело богатства экосистемы Django: подключим к нашему проекту управление изображениями.
sorl-thumbnail
В стандартную установку Django встроен инструмент для работы с картинками, но задачи вроде изменения размера изображений ему не по силам. Django умеет только загружать файлы и отдавать их как есть.
Для настоящей соцсети этого мало. Пользователи могут залить RAW-картинку с фотоаппарата размером в 5950×3968 пикселей и весом в 50 Мб, или выложить картинку-мем размером в 120х80. Если опубликовать эти картинки как есть — сайт будет выглядеть неопрятно или долго загружаться.
Дадим пользователям возможность иллюстрировать посты и сделаем так, чтобы загруженные изображения выглядели более-менее одинаково.
Одно из первых по популярности и удобству приложений для работы с графикой —
sorl-thumbnail. Для его работы нужна графическая библиотека,
sorl-thumbnail умеет работать со многими, мы возьмём библиотеку
Pillow.
Когда-то, когда Python еще набирал популярность, в компании Secret Labs AB написали библиотеку PIL (от Python Imaging Library). Это была одна из первых графических библиотек, предназначенных именно для Python. Она быстро стала стандартом в сообществе. Некоторое время спустя компания перестала существовать и развивать библиотеку: последняя версия PIL ориентирована на Python v.2.3. Но потребность в обработке изображений никуда не пропала, и Alex Clark сделал форк (ответвление проекта) под новым названием Pillow (англ. «подушка»). Очевидно, что автор дал библиотеке название, включающее буквы pil, в честь старого проекта, а не потому, что любит поспать. Новая библиотека поддерживает совместимость и для старых проектов.
Для установки Pillow выполните команду в виртуальном окружении проекта:
Скопировать код
(venv) $ pip install Pillow
Теперь установите приложение sorl-thumbnail:
Скопировать код
(venv) $ pip install sorl-thumbnail
Добавьте приложение в список INSTALLED_APPS, в конец списка:
Скопировать кодPYTHON
INSTALLED_APPS = [
'sorl.thumbnail',
]
Выполните миграцию, и после этого приложение будет готово к работе.
Теперь вам станут доступны специальные теги в шаблонах:
Скопировать код
<!-- Загрузка тегов библиотеки в шаблон -->
{% load thumbnail %}
<!-- Пример использования тега для пропорционального уменьшения и обрезки картинки до размера 100x100px с центрированием -->
<!-- и вставляет в код изображение которое отобразится пользователю. -->
{% thumbnail item.image "100x100" crop="center" as im %}
<img src="{{ im.url }}" width="{{ im.width }}" height="{{ im.height }}">
{% endthumbnail %}
Настройки проекта
Настроим модель Post так, чтобы к посту можно было добавить заглавную картинку:
Скопировать кодPYHTON
class Post(models.Model):
text = models.TextField()
pub_date = models.DateTimeField('Дата публикации', auto_now_add=True)
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='author_posts')
group = models.ForeignKey(
Group, on_delete=models.CASCADE, related_name='group_posts', blank=True, null=True
)
image = models.ImageField(upload_to='posts/', blank=True, null=True)
Аргумент upload_to указывает, куда должны загружаться пользовательские файлы.
Теперь добавьте новое поле в форму, связанную с моделью:
Скопировать кодPYTHON
from django import forms
from .models import Post
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['group', 'text', 'image']
Путь к директории для загрузки изображений указывается относительно адреса в параметре конфигурации MEDIA_ROOT, который должен содержать полный путь к директории и не должен совпадать с директорией STATIC_ROOT. Добавьте следующие строки в файл settings.py:
Скопировать кодPYTHON
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
Есть еще один нюанс настройки сервера. Мы добавляем возможность загрузки файлов пользователями, а значит, эти файлы должны быть доступны для просмотра в режиме разработки. Поэтому добавьте в файл yatube/urls.py следующий код:
Скопировать кодPYTHON
from django.conf import settings
from django.conf.urls.static import static
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
Этот код будет работать, когда ваш сайт в режиме отладки. Он позволяет обращаться файлам в директории, указанной в MEDIA_ROOT по имени, через префикс MEDIA_URL.
Не забудьте создать директорию media, добавить её в .gitignore и обновить связанную с постом форму.
Обновление шаблона
Чтобы изображение показывалось на странице сайта, измените шаблон страницы записи. Добавьте вывод картинок в post.html:
Скопировать кодHTML
<div class="card mb-3 mt-1 shadow-sm">
{% load thumbnail %}
{% thumbnail post.image "960x339" crop="center" upscale=True as im %}
<img class="card-img" src="{{ im.url }}">
{% endthumbnail %}
<div class="card-body">
...
Если в посте нет картинки, то содержимое тега thumbnail будет проигнорировано, так что проверку {% if post.image %}...{% endif %} делать не надо.
В HTML-форме создания и редактирования поста появится поле для загрузки изображения. Форма должна понимать, что из неё на сервер будут передаваться файлы. Обновите шаблон с формой — в тег <form> добавьте атрибут enctype:
Скопировать кодHTML
<form method="post" enctype="multipart/form-data">
Обновление view-функции
Осталось подправить функцию редактирования записи. Django-формы умеют работать с файлами, так что нужно лишь передать дополнительный параметр files=request.FILES or None, и больше ничего! Вам не надо отдельно сохранять файлы, не надо проверять их тип или беспокоиться, что в директории загрузки окажется файл с таким же именем (Django сам переименует файл при необходимости):
Скопировать кодPYHTON
@login_required
def post_edit(request, username, post_id):
profile = get_object_or_404(User, username=username)
post = get_object_or_404(Post, pk=post_id, author=profile)
if request.user != profile:
return redirect('post', username=username, post_id=post_id)
form = PostForm(request.POST or None, files=request.FILES or None, instance=post)
if request.method == 'POST':
if form.is_valid():
form.save()
return redirect("post", username=request.user.username, post_id=post_id)
return render(
request, 'post_edit.html', {'form': form, 'post': post},
)
Результат
У вас должна получиться страница просмотра записи с картинкой: