Сегодня поговорим о том, как реализовывается загрузка файлов с помощью Django. Для этого попробуем создать что-то типа упрощенного Инстаграма. То есть, приложение для работы с изображениями.
Установка Django в Python 3
Чтобы упростить задачу, папку с обучающим примером лучше разместить на рабочем столе. Но в целом, местоположение может быть любым. Главное, чтобы папка, в которой находится проект, находилась в легком доступе.
Откройте командную строку и создайте папку insta для хранения файлов. Чтобы установить Django и Pillow, необходимо использовать Pipenv. Pillow – это библиотека для обработки картинок. Чтобы загружать другие типы файлов, Pillow не нужен.
Давайте активируем новую виртуальную среду.
Shell $ cd ~/Desktop $ mkdir insta && cd insta $ pipenv install django==2.1.5 pillow==5.4.1 $ pipenv shell (insta) $
О том, что виртуальная среда была активирована, сообщит изменение в (insta). Также можно в любое время указать команду exit, чтобы осуществить выход, а также pipenv shell, чтобы осуществить повторный вход.
Создание проекта и приложения в Django
Создадим проект. Дадим ему имя insta_project. А приложение назовем posts.
Shell (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/imanges. В Django локацией для MEDIA_ROOT, по умолчанию является каталог, откуда все файлы пользователя и будут открываться.
В случае, если вместо картинки нужно будет загрузить новый файл, необходимо лишь заменить ImageField на FileField.
Настройка MEDIA_ROOT
Откройте insta_project/settings.py в текстовом редакторе. Нам понадобится добавить две новые конфигурации. По умолчанию MEDIA_URL и MEDIA_ROOT являются пустыми и не показываются. Поэтому требуется предварительная настройка.
- MEDIA_ROOT является путем файловой системы, куда пользователи будут загружать файлы.
- MEDIA_URL являет собой адрес сайта, который будет использоваться в файловых шаблонах.
Python
# insta_project/settings.py
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
Название media не нужно в обязательном порядке использовать. Можно выбрать любое другие имя. Но можно остановиться и на этом, поскольку оно используется по умолчанию в Django. Также нами будет создана папка 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, то можно будет зайти в админ-панель. Страница будет выглядеть следующим образом.
Далее найдите Posts и нажмите кнопку +. Здесь добавляется все, что пожелаете. Тем не менее, в данном руководстве мы создаем запись с изображением талисмана Django – пони.
После клика по кнопке «Save» пользователь будет перенаправлен на страницу «Posts», на которой размещены все записи, которые есть.
Теперь если вы откроете каталог media в проекте, то обнаружите, что в папке images появилась картинка djangopony.png. Как и было оговорено прежде, MEDIA_URL необходим как раз для этого.
Итак, с основами мы разобрались. Теперь попробуем понять то, как отображать записи, использовать urls.py, views.py, а также шаблоны файлов.
Настройка urls.py в Django
Аспектом работы с Django, который может несколько запутать, является тот факт, что зачастую для одной страницы потребуется четыре файла. Это 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
Это будет связано с представлением HomePageView, созданием которого мы займемся далее.
Представления в 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.
Python # insta_project/settings.py TEMPLATES = [ { ... 'DIRS': [os.path.join(BASE_DIR, 'templates')], # new ... }, ]
На этом все. Далее запустите сервер и перейдите на домашнюю страницу http://127.0.0.1:8000. Если в том будет необходимость, перезапустите страницу.
Ура! Если вы добавите дополнительные посты с заголовками и картинками от лица администратора, то они добавятся на главную.
Форма для добавления записи
На этом этапе возможно создание html-формы, чтобы обычные юзеры без доступа к панели администратора также имели возможность добавлять записи и выполнять загрузку файлов. Чтобы это сделать, необходимо добавить дополнительную страницу, используя форму.
Сперва начнем с файла views.py. Новое представление будет называться CreatePostView. Оно расширит CreateView, которое является компонентом Django. Также выполним импорт 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
Также возможно расширение ModelForm, который является компонентом Django. В нашем случае у базовой формы необходимо уточнить правильную модель 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).