Загрузка файлов с использованием моделей

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

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

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

Для простоты вначале определим модель новой таблицы, в которой будут храниться ссылки на загруженные файлы. Пусть она будет следующей (в файле women/models.py):

class UploadFiles(models.Model):
    file = models.FileField(upload_to='uploads_model')

Здесь фигурирует уже знакомый нам класс FileField, но взятый уже из ветки models. Поэтому это несколько иной класс и у него имеется дополнительный параметр upload_to – каталог, в который будет происходить загрузка файлов.

После определения новой модели нам нужно создать и применить миграции:

python manage.py makemigrations

python manage.py migrate

В БД появляется новая таблица и мы сейчас напрямую воспользуемся нашей моделью для сохранения файла на сервер. Для этого перейдем в файл women/views.py и найдем функцию about(). В ней после проверки корректности переданных данных, создадим объект класса модели UploadFiles с последующим вызовом метода save():

def about(request):
    if request.method == "POST":
        form = UploadFileForm(request.POST, request.FILES)
        if form.is_valid():
#            handle_uploaded_file(form.cleaned_data['file'])
            fp = UploadFiles(file=form.cleaned_data['file'])
            fp.save()
    else:
        form = UploadFileForm()
 
    return render(request, 'women/about.html', {'title': 'О сайте', 'menu': menu, 'form': form})

Давайте посмотрим, как это будет работать. Выберем файл, нажмем «Отправить» и у нас автоматически был создан каталог uploads_model и в нем размещен загруженный файл. А в таблице БД появилась запись с путем к этому файлу. Мало того, если мы попробуем еще раз загрузить тот же файл, то никаких коллизий не возникнет и ему автоматически будет присвоено другое имя. Соответственно, в таблице видим еще одну запись.

Вот так фреймворк Django позволяет автоматизировать процесс загрузки файлов на сервер. Функция handle_uploaded_file() нам теперь не нужна и ее можно удалить.

Осталось сделать один важный штрих. Мы можем на уровне всего проекта указать единую папку, в которую будут сохраняться все загружаемые файлы. Это делается в файле settings.py пакета конфигурации, в котором можно определить следующую переменную:

MEDIA_ROOT = BASE_DIR / 'media'

Теперь все файлы будут загружаться в каталог media, создавая в нем подкаталоги. Проверим это. Удалим все папки uploads, перезапустим веб-сервер и снова загрузим файл. У нас автоматически был сформирован каталог media, в нем подкаталог uploads_model, указанный в параметре upload_to='uploads_model' класса модели и в этом подкаталоге размещен выбранный файл. В таблице видим очередную запись с этой последней загрузкой. Вот так легко и просто производится сохранение произвольных данных на сервер с использованием классов моделей.

Добавление изображений к постам

Пока мы с вами использовали обычную форму для загрузки файлов. Однако совместно с моделями было бы логично применять формы в связке с ними, то есть, класс ModelForm. Давайте это сделаем.

У нас уже есть форма добавления поста по известным женщинам, которая базируется на модели Women. Поэтому, я просто добавлю в эту модель еще одно поле photo следующим образом:

photo = models.ImageField(upload_to="photos/%Y/%m/%d/", default=None, blank=True, null=True, verbose_name="Фото")

Мы здесь используем уже знакомый нам класс ImageField с параметром upload_to, который определяет путь загрузки для изображений. Обратите внимание, как он определен. Здесь на место спецификаторов %Y, %m, %d будут подставлены текущий год (четыре цифры), текущий месяц и текущий день. Это позволит нам при массовой загрузке изображений, разнести их по отдельным подкаталогам.

После изменения модели создадим и выполним миграции:

python manage.py makemigrations
python manage.py migrate 

Далее, перейдем в шаблон addpage.html. Здесь в тег формы необходимо добавить параметр:

enctype="multipart/form-data"

Иначе связанные данные не будут отправляться браузером на сервер.

В файле women/forms.py в классе AddPostForm укажем поле photo в коллекции fields:

fields = ['title', 'slug', 'content', 'photo', 'is_published', 'cat', 'husband', 'tags']

А в файле women/views.py в функции addpage() при создании модели AddPostForm по ветке POST укажем второй параметр rquest.FILES:

def addpage(request):
    if request.method == 'POST':
        form = AddPostForm(request.POST, request.FILES)
        if form.is_valid():
            # print(form.cleaned_data)
            form.save()
            return redirect('home')
    else:
        form = AddPostForm()
 
    return render(request, 'women/addpage.html', {'menu': menu, 'title': 'Добавление статьи', 'form': form})

Теперь все готово, запускаем веб-сервер, переходим на страницу добавления поста:

http://127.0.0.1:8000/addpage/

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

Рианна; rianna; Биография Рианны; Певицы

На сервере в рабочем каталоге появился папка media с набором подкаталогов и загруженным файлом изображения. В таблице women видим новую запись с заполненным полем photo.

Все, мы с вами организовали загрузку изображения для поста и связали его с конкретной записью с помощью дополнительного поля photo. На следующем занятии мы сделаем очередной шаг и посмотрим, как можно отображать ранее загруженные изображения на HTML-странице.

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

Видео по теме