Всё это время мы работали с SQLite — файловой базой данных, которая идеальна для разработки: не требует установки, не нужно настраивать подключение, вся база в одном файле. Но у SQLite есть принципиальные ограничения:
- Конкурентная запись — SQLite блокирует весь файл на запись. При нескольких одновременных запросах они выстраиваются в очередь. На продакшне с реальной нагрузкой это узкое место.
- Нет сетевого доступа — SQLite это файл на диске. Несколько серверов приложения не могут работать с одной базой одновременно.
- Ограниченный SQL — PostgreSQL поддерживает полнотекстовый поиск, JSON-поля, оконные функции и многое другое, чего нет в SQLite.
PostgreSQL — стандарт индустрии для Django-проектов на продакшне. Сегодня переводим проект на него и переносим данные.
Прежде чем менять базу данных, нужно сохранить всё, что уже есть в SQLite. Для этого Django предоставляет fixtures — механизм сериализации данных из БД в JSON (или XML/YAML), который затем можно загрузить в любую базу.
python manage.py dumpdata --output=db_backup.json --indent=2Это выгрузит все данные из всех таблиц. Но стандартные таблицы Django (сессии, типы контента, логи) при загрузке в новую БД могут создать конфликты. Лучше выгружать только нужные приложения:
# Выгрузить только данные приложения films
python manage.py dumpdata films --output=films_data.json --indent=2
# Выгрузить пользователей и группы
python manage.py dumpdata auth.user auth.group --output=users_data.json --indent=2Посмотрим на структуру файла:
[
{
"model": "films.genre",
"pk": 1,
"fields": {
"name": "Драма",
"slug": "drama"
}
},
{
"model": "films.film",
"pk": 1,
"fields": {
"title": "Крёстный отец",
"year": 1972,
"slug": "krestnyj-otec",
"rating": "9.2",
"director": 1
}
}
]Каждый объект описывается тремя ключами: model (приложение.модель), pk (первичный ключ), fields (значения полей).
python manage.py dumpdata \
--exclude=contenttypes \
--exclude=auth.permission \
--exclude=sessions \
--output=full_backup.json \
--indent=2contenttypes и auth.permission создаются автоматически при migrate на основе установленных приложений — загрузка их из фикстуры часто вызывает конфликты. Исключаем их и даём Django создать заново.
macOS:
brew install postgresql@16
brew services start postgresql@16Ubuntu/Debian:
sudo apt install postgresql postgresql-contrib
sudo systemctl start postgresql
sudo systemctl enable postgresqlWindows: Скачать установщик с postgresql.org/download/windows и запустить.
# Входим в PostgreSQL
sudo -u postgres psql # Linux
psql postgres # macOS с Homebrew-- Внутри psql
CREATE USER filmsite_user WITH PASSWORD 'ваш_надёжный_пароль';
CREATE DATABASE filmsite_db OWNER filmsite_user;
GRANT ALL PRIVILEGES ON DATABASE filmsite_db TO filmsite_user;
\qАдаптер для подключения Django к PostgreSQL:
pip install psycopg2-binarypsycopg2-binary — версия с предкомпилированными бинарниками, устанавливается без компилятора и системных зависимостей. Для продакшна рекомендуют psycopg2 (без суффикса binary) — он компилируется из исходников и лучше оптимизирован, но требует наличия PostgreSQL dev-библиотек. Для разработки psycopg2-binary достаточен.
# filmsite/settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'filmsite_db',
'USER': 'filmsite_user',
'PASSWORD': 'ваш_надёжный_пароль',
'HOST': 'localhost',
'PORT': '5432',
}
}| Параметр | Значение | Назначение |
|---|---|---|
ENGINE |
postgresql |
Использовать PostgreSQL-бэкенд |
NAME |
имя БД | База данных, созданная выше |
USER |
пользователь БД | Пользователь PostgreSQL |
PASSWORD |
пароль | Пароль пользователя PostgreSQL |
HOST |
localhost |
Сервер БД (на продакшне — IP или hostname) |
PORT |
5432 |
Стандартный порт PostgreSQL |
Как и с SMTP в уроке 25 — не храним учётные данные прямо в settings.py:
# filmsite/settings.py
import os
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ.get('DB_NAME', 'filmsite_db'),
'USER': os.environ.get('DB_USER', 'filmsite_user'),
'PASSWORD': os.environ.get('DB_PASSWORD', ''),
'HOST': os.environ.get('DB_HOST', 'localhost'),
'PORT': os.environ.get('DB_PORT', '5432'),
}
}PostgreSQL подключён, но таблиц в ней ещё нет:
python manage.py migrateDjango создаст все таблицы заново по существующим файлам миграций. База чистая — данных нет.
# Загружаем пользователей
python manage.py loaddata users_data.json
# Загружаем данные приложения
python manage.py loaddata films_data.jsonloaddata читает JSON-файл и создаёт объекты в базе данных. Порядок загрузки имеет значение: если в films_data.json есть записи Film со ссылками на Director по pk, то Director должен быть загружен раньше. Если выгрузить все данные приложения films одной командой — Django сам сохранит правильный порядок.
python manage.py shellfrom films.models import Film, Genre, Director
print(f'Фильмов: {Film.objects.count()}')
print(f'Жанров: {Genre.objects.count()}')
print(f'Режиссёров: {Director.objects.count()}')Fixtures полезны не только для миграции — это стандартный способ хранить начальные данные для тестов и для быстрого наполнения базы при разворачивании на новом сервере.
# Создать фикстуру с тестовыми жанрами
python manage.py dumpdata films.genre \
--output=films/fixtures/genres.json \
--indent=2Django ищет fixtures в папке fixtures/ каждого приложения — если там лежат файлы, их можно загрузить без указания пути:
python manage.py loaddata genres.jsonЭто удобно для автоматического заполнения базы при разворачивании проекта: добавить вызов loaddata в скрипт деплоя или Makefile.
Хорошая новость: практически ничего. Django ORM абстрагирует различия между базами данных. QuerySet'ы, фильтры, аннотации — всё работает одинаково. Несколько отличий, которые стоит знать:
Чувствительность к регистру. SQLite нечувствителен к регистру в строках. PostgreSQL — чувствителен. Если где-то использовался filter(title='крёстный отец') вместо filter(title__iexact='крёстный отец') — в PostgreSQL это даст пустой результат. Везде в нашем коде мы использовали icontains и iexact — это корректная практика, которая работает одинаково в обеих базах.
Строгие типы. PostgreSQL строго проверяет типы: нельзя вставить строку в числовое поле. SQLite был более снисходителен. Если в данных были «грязные» значения — PostgreSQL может отказать при загрузке.
Полнотекстовый поиск. PostgreSQL поддерживает SearchVector и SearchQuery из django.contrib.postgres.search — это настоящий полнотекстовый поиск с рангированием, а не просто LIKE '%...%'. Для нашего каталога это было бы заметное улучшение, но выходит за рамки базового курса.
Если в PostgreSQL уже есть данные (например, суперпользователь из createsuperuser) и загружается фикстура с теми же pk — возникнет конфликт:
django.db.utils.IntegrityError: duplicate key value violates unique constraint
Решение: загружать данные в абсолютно чистую базу (после migrate, до создания суперпользователя), либо исключать конфликтующие модели из фикстуры.
PostgreSQL использует sequences для автоинкремента id. После загрузки данных через loaddata sequence может не знать о максимальном pk из фикстуры и начать новые записи с id=1, что вызовет конфликт при следующем сохранении. Исправляется командой:
python manage.py sqlsequencereset films | python manage.py dbshellsqlsequencereset генерирует SQL для сброса последовательностей по текущим максимальным значениям id в таблицах.
Иногда старые миграции содержат конструкции, специфичные для SQLite. PostgreSQL строже к типам и может отказать при их применении. Это редкий случай для проектов, которые изначально проектировались корректно (как наш), но важно иметь это в виду при работе с чужим кодом.
- Что такое fixture в Django и для каких задач он используется?
- Почему при выгрузке данных рекомендуется исключать
contenttypesиauth.permission? - Зачем нужен
psycopg2и чем отличаетсяpsycopg2-binaryотpsycopg2? - Почему после
loaddataв PostgreSQL иногда нужно запускатьsqlsequencereset? - Что изменится в поведении
filter()при переходе с SQLite на PostgreSQL?
Тип: расширь проект
Выполни полный цикл переноса данных с SQLite на PostgreSQL.
Шаги:
- Выгрузи данные из SQLite:
films_data.json(приложениеfilms) иusers_data.json(auth.user,auth.group). При выгрузке исключиcontenttypesиauth.permission - Установи PostgreSQL и
psycopg2-binary, создай базу данныхfilmsite_dbи пользователяfilmsite_user - Обнови
DATABASESвsettings.py— вынеси учётные данные в переменные окружения - Примени миграции командой
migrate - Загрузи данные через
loaddata— сначалаfilms_data.json, потомusers_data.json - Запусти сервер и проверь, что каталог фильмов отображается корректно
- Если появились ошибки последовательностей — запусти
sqlsequencereset