В данном уроке мы задействуем базу данных, которая понадобится для создания Доски Объявления на Django, при помощи которого пользователи смогут добавлять и читать короткие объявления. Изучим мощный встроенный визуальный интерфейс администратора Django, что позволяет изменять данные, используя удобный набор инструментов. После создания тестов сохраним код на GitHub, а затем запустим приложение на Heroku.
Содержание статьи
- Начальная настройка Django приложения
- Создание модели базы данных
- Активация моделей в Django
- Как работает Панель Администратора в Django?
- Представления (Views), шаблоны (Templates) и URL
- Добавление новых записей в приложении
- Пишем тесты для доски объявления
- Сохраняем код проекта на Github
- Примеры настроек Heroku для Django проекта
- Запускаем доску объявлений на Django в Heroku
- SQLite vs PostgreSQL
Благодаря мощному ORM (Object-Relational Mapping, или объектно-реляционному отображению), в Django есть встроенная поддержка бэкенда многих баз данных: PostgreSQL, MySQL, Oracle и SQLite. Это значит, что разработчики могут написать код в файле models.py
, который потом автоматически будет перенаправлен в базу данных. Достаточно обновить секцию DATABASES в файле settings.py
.
Есть вопросы по Python?
На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!
Паблик VK
Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!
Во время локальной разработки на Django, по умолчанию использует SQLite, так как данный бэкенд ориентирован на работу с файлами, следовательно, в данном случае является наиболее подходящим. Работа с ним не требует сложной установки. Для сравнения, все остальные базы данных запрашивают отдельные от Django установки и правильную настройку.
Мы начнем использовать SQLite как локальную базу данных, а при переходе на продакшн с Heroku переключимся на PostgreSQL.
Начальная настройка Django приложения
Так как мы уже успели настроить несколько проектов Django, быстро пробежимся по всем знакомым на данный момент действиям, после чего перейдем к новым командам. Необходимые начальные шаги:
- создаем новую директорию для кода на рабочем столе с названием
mb
; - устанавливаем Django в новом виртуальном окружении;
- создаем новый проект под названием
mb_project
; - создаем новое django приложение под названием
posts
; - обновляем файл
settings.py
.
В новой командной строке вводим следующие команды:
1 2 3 4 5 6 |
$ cd ~/Desktop $ mkdir mb && cd mb $ pipenv install django==3.0.* $ pipenv shell (mb) $ django-admin startproject mb_project . (mb) $ python manage.py startapp posts |
Далее мы должны сообщить Django о новом приложении posts
. Для этого добавляем его в нижнюю часть секции INSTALLED_APPS
файла settings.py
.
1 2 3 4 5 6 7 8 9 10 |
# mb_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', # новое ] |
Затем для создания базы данных выполняем команду migrate
в терминале.
1 |
(mb) $ python manage.py migrate |
Теперь, если при помощи команды ls
заглянуть внутрь нашей директории, то можно увидеть, что там появился файл db.sqlite3
, который представляет SQLite базу данных.
1 2 3 |
(mb) $ ls Pipfile db.sqlite3 mb_project Pipfile.lock manage.py posts |
Технически файл
db.sqlite3
впервые создается при выполнении командыmigrate
илиrunserver
, однакоmigrate
синхронизирует базу данных с текущим состоянием любой модели базы данных в составе проекта и указанной вINSTALLED_APPS
. Другими словами, чтобы убедиться в правильности отображения базой данных текущего состояния проекта, вам потребуется запускатьmigrate
(а такжеmakemigrations
) при каждом обновлении модели. Вскоре рассмотрим данный вопрос подробнее.
Для подтверждения того, что все работает верно, запускаем локальный веб-сервер.
1 |
(mb) $ python manage.py runserver |
Появление знакомой страницы Django при переходе на http://127.0.0.1:8000/
говорит о том, что установлено верно.
Приветственная страница Django
Создание модели базы данных в Django
Первым делом мы должны создать модель базы данных, где мы можем хранить и отображать записи пользователей. ORM Django автоматически переведет данную модель в таблицу базы данных. В крупных проектах Django используют более сложные и взаимосвязанные между моделями баз данных, однако для нашего простого приложения доски объявлений на Django будет достаточной одной модели.
Откройте файл posts/models.py
и обратите внимание на код, который Django предоставляет по умолчанию:
1 2 3 4 |
# posts/models.py from django.db import models # Место для создания ваших моделей |
Django импортирует модуль models
, который помогает при создании новых моделей баз данных, которые сформируют характеристики данных в нашей базе. Сейчас нам нужно создать модель для хранения текстового содержимого записей доски объявлений, что можно сделать следующим образом:
1 2 3 4 5 |
# posts/models.py from django.db import models class Post(models.Model): text = models.TextField() |
Обратите внимание, мы создали новую модель базы данных под названием Post
, в которой есть текстовое поле базы данных. Мы также уточнили тип содержимого — TextField()
. Django предоставляет много различных моделей полей, поддерживающих популярные типы содержимого, например, символы, даты, целые числа, адреса электронной почты и так далее.
Активация моделей в Django
После создания новой модели ее требуется активировать. Заглядывая вперед, вне зависимости от времени создания или модификации существующей модели, нам нужно обновлять Django, что делается в два этапа:
- Первым делом при помощи команды
makemigrations
создается файл миграции. Файлы миграции создают для моделей баз данных отсылки к любым изменениям. Это значит, что, отслеживаются изменения, и в случае необходимости мы можем вовремя исправить возникшие ошибки откатив назад структуры таблиц в базе данных; - Затем мы создаем действующую базу данных при помощи команды
migrate
, которая выполняет инструкции из файла миграции.
Не забудьте покинуть локальный веб-сервер через комбинацию CTRL+C
в командной строке, после чего выполните две следующие команды:
1 2 |
(mb) $ python manage.py makemigrations posts (mb) $ python manage.py migrate |
Стоит отметить, что добавлять название модели после makemigrations
вовсе не обязательно. Если просто запустить python manage.py makemigrations
, тогда файл миграции будет создан для всех доступных изменений в проекте Django. В случае работы с маленьким проектом вроде нашего, где только одно приложение, это не играет особой роли, однако обычно в проектах Django задействовано куда больше приложений! Следовательно, если вы сделаете изменения в модели с несколькими приложениями, итоговый файл миграции будет включать все эти изменения! Вариант не лучший. Файл миграций должен быть настолько мал и ясен, насколько это возможно, так как это значительно облегчает отладку или возврат к предыдущим версиям кода.
По этой причине будет лучше выработать у себя привычку во время выполнения команды
makemigrations
всегда добавлять название приложения в конец строки.
Как работает Панель Администратора в Django?
Одной из самых сильных сторон Django является его встроенный интерфейс администратора, который предоставляет визуальный набор инструментов для работы с данными. По большей части это стало следствием того, что изначально Django разрабатывался в качестве CMS (системы управления содержимым) для газет. Идея состояла в том, чтобы у журналистов была возможность самостоятельно писать и редактировать статьи в админке без необходимости касаться «кода». Со временем встроенное приложение интерфейса администратора претерпело серьезные изменения, став мощным инструментом для управления всеми аспектами проектов на Django.
Для использования панели администратора Django требуется создать аккаунт супер-пользователя, который будет иметь право войти в админку используя логин и пароль. В командной строке наберите python manage.py createsuperuser
, после чего укажите имя пользователя, электронную почту и пароль:
1 2 3 4 5 6 |
(mb) $ python manage.py createsuperuser Username (leave blank to use 'wsv'): wsv Email: Password: Password (again): Superuser created successfully. |
Обратите внимание, что во время ввода пароля он не отображается в консоли командной строки. Это сделано в целях обеспечения безопасности.
Перезагрузите сервер Django при помощи python manage.py runserver
, после чего зайдите в браузер и перейдите по адресу http://127.0.0.1:8000/admin/
. Откроется страница входа в админку:
Страница входа в админку
Зайдите используя созданные ранее логин и пароль. Откроется домашняя страница панели администратора:
Домашняя страница панели администратора
Где приложение
posts
? Оно почему-то не показывается на главной странице админки!
Как и в случае с добавлением новых приложений в конфигурацию INSTALLED_APPS
, здесь нам понадобится обновить файл admin.py
, тогда оно появится в админке.
В текстовом редакторе откройте posts/admin.py
и для отображения модели Post
добавьте следующий код:
1 2 3 4 5 6 |
# posts/admin.py from django.contrib import admin from .models import Post admin.site.register(Post) |
Теперь Django знает, что ему нужно показать приложение posts
и его модель базы данных Post
в админке. Для отображения данных элементов требуется перезагрузить страницу:
Обновленная главная страница панели администратора
Создадим первую запись доски объявлений. Нажмите кнопку + Add
из блока Posts
и напишите что-нибудь в текстовом поле.
Новая запись в админке
Далее нажмите кнопку «Save», после чего вы будете перенаправлены на главную страницу Post
. Если присмотреться, то появилась проблема: новая запись называется «Post object«, что ничего не говорит о самой записи!
Новая запись в админке
Давайте исправим положение. В файле posts/models.py
добавим новую функцию __str__
:
1 2 3 4 5 6 7 8 9 |
# posts/models.py from django.db import models class Post(models.Model): text = models.TextField() def __str__(self): return self.text[:50] |
Теперь в поле text будут отображаться первые 50 символов. Если перезагрузить в браузере данную страницу, то можно увидеть, что название стало более содержательным и полезным для понимания темы записи.
Новая запись в админке
Намного лучше! Стоит взять себе в привычку добавлять метод __str__
для всех моделей — это повысит читабельность проекта.
Представления (Views), шаблоны (Templates) и URL в Django
Для отображения содержимого базы данных на домашней странице требуется подключить представления, шаблоны и URLConfs. К текущему моменту, данный паттерн уже должен стать вам знакомым.
Начнем с представлений (Views). Ранее мы использовали встроенный генерик Template-View для отображения файла шаблона на домашней странице. Нам нужно составить список содержимого модели рассматриваемой базы данных. К счастью, в веб-разработке задачи подобного рода не редкость — в Django для этого предусмотрен классовый генерик ListView.
В файл posts/views.py
введите следующий код:
1 2 3 4 5 6 7 |
# posts/views.py from django.views.generic import ListView from .models import Post class HomePageView(ListView): model = Post template_name = 'home.html' |
На первой строке импортируется ListView
, а на второй строке импортируется модель Post
. В представлении HomePageView
мы наследуем ListView
, а также уточняем нужную нам модель (Post) и шаблон (home.html).
Первое представление готово, а это значит, что нам все еще требуется настроить URL и создать HTML файл шаблона. Начнем с шаблона. Создаем директорию под названием templates
, а внутри нее HTML файл шаблона home.html
.
1 2 |
(mb) $ mkdir templates (mb) $ touch templates/home.html |
Затем обновляем поле DIRS
в файле settings.py
, чтобы Django искал шаблоны и в эту директорию.
1 2 3 4 5 6 7 8 |
# mb_project/settings.py TEMPLATES = [ { ... 'DIRS': [os.path.join(BASE_DIR, 'templates')], # новое ... }, ] |
ListView автоматически возвращает переменную с записями под названием object_list
, которую можно включить в цикл через встроенный тег шаблона {% for %}. В цикле мы создадим переменную под названием post
, после чего получаем доступ к полю, которое нужно отобразить post.text
.
1 2 3 4 5 6 7 |
<!-- templates/home.html --> <h1>Message board homepage</h1> <ul> {% for post in object_list %} <li>{{ post.text }}</li> {% endfor %} </ul> |
Название переменной object_list
многим может показаться не очень подходящим. При помощи атрибута context_object_name
её можно переименовать. Здесь Django вновь показывает гибкость возможностей настройки.
Вернемся к файлу posts/views.py
и добавим следующее:
1 2 3 4 5 6 7 8 9 |
# posts/views.py from django.views.generic import ListView from .models import Post class HomePageView(ListView): model = Post template_name = 'home.html' context_object_name = 'all_posts_list' # новое изменение |
Добавление более понятных названий для переменных позволяет другим членам команды разработки, например, дизайнерам, быстрее вникать в суть кода и иметь представление что содержится в той или иной переменной в зависимости от её названия.
Также, не забудьте обновить шаблон, чтобы он отсылался на all_posts_list
, а не на object_list
.
1 2 3 4 5 6 7 |
<!-- templates/home.html --> <h1>Message board homepage</h1> <ul> {% for post in all_posts_list %} <li>{{ post.text }}</li> {% endfor %} </ul> |
На последнем этапе требуется настроить URLConfs. Начнем с файла mb_project/urls.py
, где нужно просто включить posts
добавив его через include()
на второй строке.
1 2 3 4 5 6 7 8 |
# mb_project/urls.py from django.contrib import admin from django.urls import path, include # новое добавление urlpatterns = [ path('admin/', admin.site.urls), path('', include('posts.urls')), # новое добавление ] |
Далее создаем файл настройки URL маршрутов приложений url.py
:
1 |
(mb) $ touch posts/urls.py |
Обновляем его:
1 2 3 4 5 6 7 8 |
# posts/urls.py from django.urls import path from .views import HomePageView urlpatterns = [ path('', HomePageView.as_view(), name='home'), ] |
Перезагружаем веб-сервер через python manage.py runserver
и переходим на домашнюю страницу http://127.0.0.1:8000/
, где появился список записей доски объявлений.
Домашняя страница с записями
Мы практически подошли к финалу. Давайте создадим еще несколько записей на доске объявлений через админку Django, чтобы убедиться в правильности их отображения на домашней странице.
Добавление новых записей в приложении Django
Для добавления новых записей на доску объявлений вернемся в панель администратора и создадим еще две записи. У нас все выглядит следующим образом:
Создание новой записи через админку
Создаем третью запись через админку
Обновленная секция записей в админке
По возвращении на домашнюю страницу, вы увидите, что на ней теперь отображаются отформатированные записи. Отлично!
Домашняя страница с тремя записями
Все работает, поэтому пришло время для загрузки нашего кода в репозиторий на github.
Командная строка
1 2 3 |
(mb) $ git init (mb) $ git add -A (mb) $ git commit -m 'initial commit' |
Пишем TestCase тесты для доски объявления на Django
Ранее мы уже тестировали статичные страницы, для чего использовали SimpleTestCase. Однако теперь домашняя страница работает с базой данных, поэтому требуется задействовать TestCase, что позволит создать тестовую базу данных для проверки. Иными словами, пропадает необходимость запускать тесты на действующей базе данных. Вместо этого создается отдельная тестовая база данных, наполняется образцами данных, после чего выполняется проверка. Данный подход является наиболее безопасным и производительным.
Начнем с добавления пробной записи, а затем проверим, правильно ли оно сохранилась в базе данных. Важно, чтобы все тестовые методы начинались с префикса test_
. Таким образом Django будет знать, что проверять. Код выглядит следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# posts/tests.py from django.test import TestCase from .models import Post class PostModelTest(TestCase): def setUp(self): Post.objects.create(text='just a test') def test_text_content(self): post = Post.objects.get(id=1) expected_object_name = f'{post.text}' self.assertEqual(expected_object_name, 'just a test') |
В верхней части мы импортировали модуль TestCase
, который дает возможность создать образец базы данных и таблицу модели Post
. Мы создали новый класс PostModelTest
и добавили метод setUp
для создания новой базы данных, у которой только одна запись — это пост с текстовым полем, содержащим строку «just a test».
Теперь можно запустить первый тест test_text_content
для проверки того, что поле в базе данных действительно содержит фразу just a test
. Мы создали переменную post
, которая содержит запись с ID = 1 в нашей модели Post
.
Обратите внимание, что Django автоматически настраивает
id
. Если мы добавим новую запись, то ееid
будет 2, у следующей за нейid
станет 3 и так далее.
Следующая строка использует f-строки, ставшие отличным введением в Python 3.6, что позволило добавлять переменные напрямую в строки — главное, чтобы данные переменные находились в фигурных скобках {}
. Здесь мы указываем, что в expected_object_name
будет значение от post.text
, т.е. фраза just a test
.
На последней строке кода мы используем assertEqual, что проверяет, действительно ли заново созданная запись совпадает с тем, что мы добавили сверху. Запустите тест через командную строку при помощи команды python manage.py test
.
1 2 3 4 5 6 7 8 9 |
(mb) $ python manage.py test Creating test database for alias 'default'... System check identified no issues (0 silenced). . ---------------------------------------------------------------------- Ran 1 test in 0.001s OK Destroying test database for alias 'default'... |
Тест пройден!
Предыдущее объяснение могло показаться несколько перегруженным информацией, но не беспокойтесь. При начальной ознакомлении с тестами это естественно, в будущем данный процесс станет привычным и даже репетативным.
Пришло время провести иные типы тестов. Первый тест анализировал модель, а теперь мы оценим качество самой домашней страницы:
- она действительно существует и возвращает HTTP код состояния 200?
- она использует
HomePageView
как представление? - она использует файл
home.html
в качестве шаблона?
Можно включить все вышеупомянутые тесты в новый класс под названием HomePageViewTest
. Обратите внимание, что для получения прямого доступа к названию представления мы импортируем reverse и обращаемся к названному URL home
. Зачем использовать такой способ? URL структура меняется на протяжении разработки проекта, однако именованные URL, как правило, этого не делают.
Нам нужно добавить импорт reverse
и создать новый класс HomePageViewTest
для теста.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# posts/tests.py from django.test import TestCase from django.urls import reverse # новое from .models import Post class PostModelTest(TestCase): ... class HomePageViewTest(TestCase): # новое def setUp(self): Post.objects.create(text='this is another test') def test_view_url_exists_at_proper_location(self): resp = self.client.get('/') self.assertEqual(resp.status_code, 200) def test_view_url_by_name(self): resp = self.client.get(reverse('home')) self.assertEqual(resp.status_code, 200) def test_view_uses_correct_template(self): resp = self.client.get(reverse('home')) self.assertEqual(resp.status_code, 200) self.assertTemplateUsed(resp, 'home.html') |
Если вы запустите тесты, то увидите, что все работает.
1 2 3 4 5 6 7 8 9 |
(mb) $ python manage.py test Creating test database for alias 'default'... System check identified no issues (0 silenced). . ---------------------------------------------------------------------- Ran 4 tests in 0.036s OK Destroying test database for alias 'default'... |
Почему в выводе упоминается четыре теста, а не шесть? Дело в том, что методы setUp
по сути не являются тестами — они считаются вспомогательными функциями. Только функции, которые начинаются с test*
и находятся внутри файла tests.py
будут запущены как тесты после выполнения команды python manage.py test
.
На этом с редактированием кода для тестов закончим и закоммитим изменения на git.
1 2 |
(mb) $ git add -A (mb) $ git commit -m 'added tests' |
Сохраняем код проекта на Github
Нам нужно сохранить код на GitHub. У вас уже должен быть GitHub аккаунт, осталось только создать новое хранилище — назовем его mb-app
. Не забудьте нажать кнопку «Private».
На следующей странице пролистайте до строчки «…or push an existing repository from the command line». Скопируйте и вставьте две указанные команды в терминал. Результат должен быть как в примере ниже, только вместо моего имени пользователя на GitHub (wsincent
) будет указано ваше.
1 2 |
(mb) $ git remote add origin https://github.com/wsvincent/mb-app.git (mb) $ git push -u origin master |
Примеры настроек Heroku для Django проекта
У вас уже должен быть настроенный аккаунт от Heroku. Теперь для размещения нашей доски объявлений в интернете, нам нужно сделать определенные изменения в файлах проекта:
- обновить файл
Pipfile.lock
; - добавить новый
Procfile
; - установить
Gunicorn
; - обновить
settings.py
.
В Pipfile
уточните Python версию — в нашем случае это 3.7. В нижнюю часть файла добавьте следующие две строчки.
Pipfile
1 2 |
[requires] python_version = "3.7" |
Для генерации подходящего Pipfile.lock
запустите pipenv lock
.
1 |
(mb) $ pipenv lock |
Далее нужно создать Procfile
, что укажет Heroku, как запускать удаленный сервер, при помощи которого заработает наш код.
1 |
(mb) $ touch Procfile |
Мы говорим Heroku, что нужно использовать Gunicorn в качестве веб-сервера и изучить файл mb_project.wsgi
для дальнейших инструкций.
Procfile
1 |
web: gunicorn mb_project.wsgi --log-file - |
Далее устанавливаем Gunicorn, что будет задействован в качестве WSGI HTTP сервера. Внутренний веб-сервер в Django по-прежнему используем при локальной разработке.
1 |
(mb) $ pipenv install gunicorn==19.9.0 |
Обновляем ALLOWED_HOSTS
в файле settings.py
.
1 2 |
# mb_project/settings.py ALLOWED_HOSTS = ['*'] |
Вот и все. Добавляем и коммитим новые изменения через git, после чего размещаем код на GitHub.
1 2 3 4 |
(mb) $ git status (mb) $ git add -A (mb) $ git commit -m 'New updates for Heroku deployment' (mb) $ git push -u origin master |
Запускаем доску объявлений на Django в Heroku
Убедитесь, что вы зашли на верный аккаунт Heroku.
1 |
(mb) $ heroku login |
Запустите команду create
, тогда Heroku случайным образом сгенерирует название приложения.
1 2 3 4 |
(mb) $ heroku create Creating app... done, ⬢ sleepy-brook-64719 https://sleepy-brook-64719.herokuapp.com/ | https://git.heroku.com/sleepy-brook-64719.git |
Теперь скажем Heroku игнорировать статические файлы.
1 |
(mb) $ heroku config:set DISABLE_COLLECTSTATIC=1 |
Разместите код на Heroku и не забудьте добавить свободное масштабирование, иначе сайт не запустится, код будет просто лежать без дела.
1 2 |
(mb) $ git push heroku master (mb) $ heroku ps:scale web=1 |
Можете открыть URL нового проекта из командной строки, набрав heroku open
, что запустит новое окно браузера. Вот что получилось у нас:
Рабочий сайт
SQLite vs PostgreSQL
Сейчас наше приложение работает с SQLite — как локально, так и на Heroku. Тем не менее, возникает проблема: легкость SQLite обусловлена зависимостью от файловой системы, которая на Heroku обновляется каждые 24 часа! Это значит, что данные, добавленные в запущенную базу данных, довольно скоро будут удалены.
Для запущенного рабочего приложения решением проблемы станет переключение на готовый к работе бэкенд, такой как PostgreSQL, что будет также использован и локально. В продакшене на Heroku его можно использовать напрямую через Postgres add-on, у которого даже есть бесплатный план «Hobby Dev». Неудобство заключается в том, что для локального использования PostgreSQL требуется дополнительная настройка конфигурации, которая будет сложна для новичка.
Заключение
Мы создали, протестировали и разместили наше первое приложение с базой данных. На простом примере было показано, как создать модель базы данных, обновить ее при помощи панели администратора, а затем отобразить содержимое на веб-странице. Однако, чего-то не хватает, не так ли?
На практике, пользователи используют разнообразные формы для взаимодействия с сайтом. Не у каждого ведь есть доступ к панели администратора. В следующем уроке мы займемся созданием блога на Django, что будет использовать формы, при помощи которых пользователи смогут создавать, редактировать и удалять записи. Для оптимизации интерфейса мы также добавим CSS стили.
Являюсь администратором нескольких порталов по обучению языков программирования Python, Golang и Kotlin. В составе небольшой команды единомышленников, мы занимаемся популяризацией языков программирования на русскоязычную аудиторию. Большая часть статей была адаптирована нами на русский язык и распространяется бесплатно.
E-mail: vasile.buldumac@ati.utm.md
Образование
Universitatea Tehnică a Moldovei (utm.md)
- 2014 — 2018 Технический Университет Молдовы, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Технический Университет Молдовы, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»