Создаем форму загрузки файлов в Django
В данном руководстве показано, как реализовать загрузку файлов с Django. Для этого создадим простой клон Instagram, который будет работать с изображениями.
Установка Django в Python 3
Для простоты папку с обучающим примером лучше поместить на рабочем столе, но в общем и целом расположение особого значения не имеет. Главное, чтобы директория проекта была легко доступной.
Откройте командную строку и создайте директорию insta
для хранения файлов. Для установки как Django, так и Pillow мы будем использовать Pipenv . Pillow является библиотекой для обработки изображений. Для загрузки других типов файлов Pillow не понадобится.
Активируем новую виртуальную среду:
$ cd ~/Desktop $ mkdir insta && cd insta $ pipenv install django==2.1.5 pillow==5.4.1 $ pipenv shell (insta) $
Об активации виртуальной среды сообщит изменение в (insta)
. Вы также можете в любое время ввести команду exit
для выхода и pipenv shell
для повторного входа.
Создание проекта и приложения в Django
Создадим новый проект Django под названием insta_project
и новое приложение, которое назовем posts
.
(insta) $ django-admin startproject insta_project . (insta) $ python manage.py startapp posts
Так как мы добавили новое приложение, мы должны сообщить об этом Django в нижней части конфигурации INSTALLED_APPS
в settings.py
.
# insta_project/settings.py INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'posts.apps.PostsConfig', # новое ]
Теперь запускаем python manage.py migrate
для установки базы данных нового проекта.
(insta) $ python manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying sessions.0001_initial... OK
Создаем модели в Django
Лучше всего начинать с модели базы данных. В нашем случае у модели Post
будет только два поля: title
и cover
. Ниже мы также добавим метод __str__()
, чтобы title
отобразился в интерфейсе администратора Django.
# posts/models.py from django.db import models class Post(models.Model): title = models.TextField() cover = models.ImageField(upload_to='images/') def __str__(self): return self.title
Местоположение загружаемых файлов image
будет в MEDIA_ROOT/images
. В Django локацией для MEDIA_ROOT
по умолчанию является папка, откуда будут загружаться все файлы пользователя.
В случае, когда вместо изображения требуется загрузить другой файл, нужно просто поменять ImageField
на FileField
.
Настройка MEDIA_ROOT в Django 2
Откройте insta_project/settings.py
в вашем текстовом редакторе. Нам потребуется добавить две новые конфигурации. По умолчанию MEDIA_URL
и MEDIA_ROOT
являются пустыми и не отображаются на экране, поэтому их необходимо настроить:
MEDIA_ROOT
является путем файловой системы, куда пользователи будут загружать файлы;MEDIA_URL
представляет собой URL, который мы можем использовать в шаблонах для файлов.
# insta_project/settings.py MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
Название media
использовать не обязательно, можно выбрать любое, просто в Django media
используется по умолчанию. Мы также создаем папку images
внутри для простоты навигации.
(insta) $ mkdir media (insta) $ mkdir media/images
Панель администратора (админка) в Django
Сейчас мы обновим файл posts/admin.py
, после чего в Django появится возможность использовать приложение Post
от имени администратора.
# posts/admin.py from django.contrib import admin from .models import Post admin.site.register(Post)
Все настроено! Генерируем новый файл миграции.
(insta) $ python manage.py makemigrations Migrations for 'posts': posts/migrations/0001_initial.py - Create model Post
Теперь запускаем migrate
для обновления базы данных.
(insta) $ python manage.py migrate Operations to perform: Apply all migrations: admin, auth, contenttypes, posts, session s Running migrations: Applying posts.0001_initial... OK
Теперь можно создать аккаунт superuser
для доступа к интерфейсу администратора, после чего выполнить runserver
для первого запуска локального веб-сервера:
(insta) $ python manage.py createsuperuser (insta) $ python manage.py runserver
Если набрать в адресной строке браузера http://127.0.0.1:8000/admin, появится возможность зайти в админку Django. Вы будете направлены на следующую страницу:
Нажмите на + Add
возле Posts
. Здесь можно добавить все, что хотите, однако в данном руководстве мы создаем запись с изображением талисмана Django — пони.
Скачать картинку можно тут Django Pony.
После нажатия «Save» вы будете перенаправлены на страницу Posts
, где расположены все имеющиеся записи.
Теперь если вы загляните в папку media
в вашем проекте, то увидите, что в директории images
появилось изображение djangopony.png
. Как и было сказано ранее, MEDIA_URL
нужен именно для этого.
Итак, с основами мы разобрались. Теперь разберемся с тем, как отображать записи, использовать urls.py
, views.py
и шаблоны файлов.
Настройка urls.py в Django
Аспектом работы с Django, который может несколько запутать, является тот факт, что зачастую для одной веб-страницы требуется 4 разных, но взаимосвязанных файла: models.py
, urls.py
, views.py
и html-шаблоны. Здесь мы будем разбирать понятия в следующем порядке: модели (models) -> urls -> представления (views) -> шаблоны (templates). С моделью мы уже разобрались, так что перейдем к URL.
Нам понадобятся обновить файл urls.py
. Вначале на проектном уровне insta_project/urls.py
мы добавим импорты для settings
, include
и static
.
После этого мы определим путь для приложения posts
. Стоит отметить, что если настройки в режиме DEBUG
, то MEDIA_URL
также нужно добавить. В противном случае не получится увидеть загружаемые изображения.
# insta_project/urls.py from django.contrib import admin from django.conf import settings # new from django.urls import path, include # new from django.conf.urls.static import static # new urlpatterns = [ path('admin/', admin.site.urls), path('', include('posts.urls')), # new ] if settings.DEBUG: # new urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Далее необходимо рассортировать пути URL в пределах приложения posts
. Первым делом создается файл через linux команду touch.
(insta) $ touch posts/urls.py
Затем мы вынесем все записи на главную страницу, для этого используем пустую строку ''
в качестве пути.
# posts/urls.py from django.urls import path from .views import HomePageView urlpatterns = [ path('', HomePageView.as_view(), name='home'), ]
Это будет связано с представлением (view) HomePageView
, созданием которого мы займемся далее.
Представления views в Django
Здесь можно использовать обычный ListView
, основанный на классе, а затем импортировать модель Post
. После этого надо создать HomePageView
, что использует данную модель, а также шаблон под названием home.html
.
# posts/views.py from django.views.generic import ListView from .models import Post class HomePageView(ListView): model = Post template_name = 'home.html'
Теперь переходим к файлу-шаблону под названием home.html
.
Шаблоны templates в Django
При выборе локации для шаблона есть два варианта. Мы могли бы поместить его в posts
, что расположен в posts/templates/posts/home.html
, но тогда структура станет избыточной. Кроме того, если шаблоны расположены глубоко в папках своих приложений, их будет сложнее разбирать. Именно поэтому для урока на проектном уровне мы создадим отдельную директорию templates
.
$ mkdir templates $ touch templates/home.html
Далее укажем Django, чтобы он также рассматривал данную директорию при поиске шаблонов, обновив конфигурацию TEMPLATES
в insta_project/settings.py
.
# insta_project/settings.py TEMPLATES = [ { ... 'DIRS': [os.path.join(BASE_DIR, 'templates')], # new ... }, ]
Файл шаблона home.html
показывает title
и image
всех наших записей. В Instagram все было бы так же.
<!-- templates/home.html --> <h1>Django Image Uploading</h1> <ul> {% for post in object_list %} <h2>{{ post.title }}</h2> <img src="{{ post.cover.url}}" alt="{{ post.title }}"> {% endfor %} </ul>
Вот и все. Запустите сервер командой python manage.py runserver
и перейдите на домашнюю страницу http://127.0.0.1:8000. В случае необходимости перезагрузите страницу.
Вуаля! Если вы добавите дополнительные посты с заголовками и картинками от имени администратора, то они появятся на домашней странице.
Форма для добавления записи в Django
Теперь мы можем создать html форму для того, чтобы обычные пользователи без доступа в админку могли также добавлять записи и загружать файлы. Это предполагает создание новой страницы при помощи формы.
Давайте начнем с файла views.py
. Назовем новое представление CreatePostView
. Оно расширит встроенное в Django CreateView. Также импортируем reverse_lazy
, который отвечает за возвращение на домашнюю страницу после отправки формы через POST запрос.
В представлении мы указываем model
, form_class
, что будет создан далее, template_name
и, конечно же, success_url
, который будет получен после отправки.
# posts/views.py from django.views.generic import ListView, CreateView # новый from django.urls import reverse_lazy # новый from .forms import PostForm # новый from .models import Post class HomePageView(ListView): model = Post template_name = 'home.html' class CreatePostView(CreateView): # новый model = Post form_class = PostForm template_name = 'post.html' success_url = reverse_lazy('home')
Займемся формой. Она создается следующим образом:
(insta) $ touch posts/forms.py
Мы можем расширить встроенный в Django ModelForm. Здесь у базовой формы потребуется уточнить правильную модель Post
и названия выводимых на экран полей. В данном случае это title
и cover
.
# posts/forms.py from django import forms from .models import Post class PostForm(forms.ModelForm): class Meta: model = Post fields = ['title', 'cover']
Для формы создается специальная страницы, URL-путь которой post/
.
# posts/urls.py from django.urls import path from .views import HomePageView, CreatePostView # new urlpatterns = [ path('', HomePageView.as_view(), name='home'), path('post/', CreatePostView.as_view(), name='add_post') # new ]
Создаем новый шаблон.
(insta) $ touch templates/post.html
Вносим в него заголовок и форму. Для защиты важно всегда добавляйте csrf_token
. Уточняем form.as_p
, из-за чего Django выведет каждое поле в виде отдельного параграфа.
<!-- templates/post.html --> <h1>Create Post Page</h1> <form method="post" enctype="multipart/form-data"> {% csrf_token %} {{ form.as_p }} <button type="submit">Submit New Post</button> </form>
Вот и оно! Убедитесь, что сервер запущен, и перейдите на страницу http://127.0.0.1:8000/post/.
После подтверждения создания новой записи вы будете перенаправлены на домашнюю страницу, где отображаются все имеющиеся посты.
Следующие шаги
И что теперь? Многим наверняка захочется наложить определенные ограничения на размер картинки. Это можно сделать в файле models.py
или при помощи CSS. Также многим наверняка захочется добавить опции редактирования или удаления для записей.
Ввиду важного обновления, не рекомендуется размещать файлы в Django. Лучше настроить выделенную внешнюю службу, например, сеть доставки контента Content Delivery Network (CDN).