Django — добавление картинок к постам



Django — это огромная экосистема из всевозможных модулей, расширяющих возможности базового фреймворка. На сайте PyPI.org размещены десятки тысяч расширений, в названии которых есть слово django, а неопубликованных модулей или тех, что названы как-то иначе — ещё больше.

Пустим в дело богатства экосистемы: подключим к проекту Yatube управление изображениями.

Приложение для работы с картинками: sorl-thumbnail

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

Для настоящей соцсети этого мало. Пользователи могут залить RAW-картинку размером 5950×3968 пикселей и весом в 50 Мб, или выложить картинку-мем размером в 120х80. Если опубликовать эти картинки в исходном виде — сайт будет выглядеть неопрятно или долго загружаться.

Дадим пользователям возможность иллюстрировать посты и сделаем так, чтобы загруженные изображения выглядели более-менее одинаково.Одно из первых по популярности и удобству приложений для работы с графикой — библиотека sorl-thumbnail. Для его работы нужна графическая библиотека. Для sorl-thumbnail годятся многие библиотеки, мы возьмём Pillow.

Когда Python ещё набирал популярность, в компании Secret Labs AB написали библиотеку PIL (от Python Imaging Library). Это была одна из первых графических библиотек для Python. Она быстро стала стандартом в сообществе. Компания Secret Labs AB перестала существовать, но потребность в обработке изображений никуда не пропала, и Alex Clark сделал форк (ответвление проекта) под новым названием Pillow (англ. «подушка»); буквы pil включены в название в честь старого проекта, а не потому, что Кларк любит поспать. Новая библиотека поддерживает совместимость и со старыми проектами.

Для установки Pillow выполните команду в виртуальном окружении проекта:

(venv) $ pip install pillow 

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

Теперь установите приложение sorl-thumbnail:

(venv) $ pip install sorl-thumbnail 

Добавьте приложение в список INSTALLED_APPS, в конец списка:

PYTHONINSTALLED_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 новое поле, чтобы к посту можно было добавить заглавную картинку:

PYTHON
class Post(models.Model):
    text = models.TextField(
        'Текст поста',
        help_text='Введите текст поста'
    )
    pub_date = models.DateTimeField(
        'Дата публикации',
        auto_now_add=True
    )
    author = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        verbose_name='Автор'
    )
    group = models.ForeignKey(
        Group,
        on_delete=models.SET_NULL,
        related_name='posts',
        blank=True,
        null=True,
        verbose_name='Группа',
        help_text='Выберите группу'
    )
    # Поле для картинки (необязательное) 
    image = models.ImageField(
        'Картинка',
        upload_to='posts/',
        blank=True
    )  
    # Аргумент upload_to указывает директорию, 
    # в которую будут загружаться пользовательские файлы. 

    class Meta:
        ordering = ('-pub_date',)
        verbose_name = 'Пост'
        verbose_name_plural = 'Посты'

    def __str__(self):
        return self.text[:15] 

Путь в параметре upload_to указывается относительно адреса, указанного в settings.py в MEDIA_ROOT: в нём устанавливают полный путь к директории, куда будут загружаться файлы пользователей: обычно её называют media/.

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

Добавьте следующие строки в файл settings.py:

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media') 

Для загрузки картинок установлен параметр upload_to='posts/', таким образом картинки, прикреплённые к постам, будут сохраняться в директории media/posts.

Как всегда, после изменения модели сделайте миграции.

Теперь добавьте новое поле в форму, связанную с моделью:

from django import forms

from .models import Post

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        # Добавили поле image в форму
        fields = ('group', 'text', 'image') 

При дефолтных настройках проекта в режиме разработчика Django не будет раздавать картинки, загруженные пользователями: обычно раздачей картинок занимается специальный сервер. Чтобы настроить отображение картинок — в urls.py нужно переопределить поведение Django — указать, что в режиме DEBUG=True он должен брать картинки из директории, указанной в MEDIA_ROOT.

Добавьте в файл yatube/urls.py следующий код:

# Эти строки — в начало файла, после импорта других модулей
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
    )
 

Эта колдограмма будет работать, когда ваш сайт в режиме отладки. Он позволяет обращаться к файлам в директории, указанной в MEDIA_ROOT по имени, через префикс MEDIA_URL.

Создайте директорию media/ вручную и добавьте её в .gitignore.

Обновление шаблона

Чтобы изображение показывалось на странице сайта, измените шаблон страницы записи. Добавьте вывод картинок в posts/post_detail.html:

{% load thumbnail %}
  ...
<article>
  <ul>
    <li>
      Автор: {{ post.author.get_full_name }} 
      <a href="{% url 'posts:profile' post.author %}">все посты пользователя</a>
    </li>
    <li>
      Дата публикации: {{ post.pub_date|date:"d E Y" }}
    </li>
  </ul>
  {% thumbnail post.image "960x339" crop="center" upscale=True as im %}
    <img class="card-img my-2" src="{{ im.url }}">
  {% endthumbnail %}
  <p>{{ post.text }}</p>
  <a href="{% url 'posts:post_detail' post.pk %}">подробная информация</a>
</article>
... 

Если в посте нет картинки, то содержимое тега thumbnail будет проигнорировано; проверку {% if post.image %}...{% endif %} делать не надо.

Шаблон формы создания и редактирования поста

В HTML-форме создания и редактирования поста появится поле для загрузки изображения. Форма должна понимать, что из неё на сервер будут передаваться файлы. Обновите шаблон с формой — в HTML-тег <form> добавьте атрибут enctype:

<form method="post" enctype="multipart/form-data">
  {% csrf_token %}
  {% for field in form %}
    ...
  {% endfor %} 

Обновление view-функции

Осталось подправить view-функцию редактирования записи. Django-формы умеют работать с файлами. Нужно лишь передать дополнительный параметр files=request.FILES or None, и больше ничего! Вам не надо отдельно сохранять файлы, не надо проверять их тип или беспокоиться, что в директории загрузки окажется файл с таким же именем — Django сам переименует файл при необходимости:

def post_edit(request, post_id):
    post = get_object_or_404(Post, pk=post_id)
    if post.author != request.user:
        return redirect('posts:post_detail', post_id=post_id)

    form = PostForm(
        request.POST or None,
        files=request.FILES or None,
        instance=post
    )
    if form.is_valid():
        form.save()
        return redirect('posts:post_detail', post_id=post_id)
    context = {
        'post': post,
        'form': form,
        'is_edit': True,
    }
    return render(request, 'posts/create_post.html', context) 


Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: