Переход на PostgreSQL с MySQL в Django

В этой статье можно узнать о шагах, которые нужно предпринять, чтобы перенести проекты Django с MySQL на PostgreSQL.

База данных MySQL — хорошее начало для маленьких и средних проектов. Она известен и широко используется, имеет хорошую документацию. Также есть отличные клиенты для простого управления, такие как phpMyAdmin (web), HeidiSQL (Windows) или Sequel Pro (macOS).

Когда кто-то начинает задумываться о масштабировании проекта, то нужно выбирать что-то более подходящее. Это должна быть БД, которая быстрая, надежная и поддерживает стандарты ANSI для реляционных баз данных. То, что использует большинство разработчиков Django. И такой базой данных для большинства профессионалов является PostgreSQL. PostgreSQL позволяет использовать несколько специфичных функций от разработчиков, которые не доступны в MySQL, например многомерные массивы, поля JSON, поля пары ключ-значение, специальные нечувствительные к регистру текстовые поля, поля диапазонов, специальные индексы, нормальный полнотекстовый поиск и т.п. Для новичка, который хочет быстро влиться в разработку с использованием PostgreSQL, лучший клиент БД — pgAdmin (macOS, linux, windows ). Он может показаться слишком сложным сначала (по сравнению с клиентами MySQL), но поскольку в Django есть встроенная админка и удобный ORM, не будет частой необходимостью проверять базу данных в ее "родном" формате.

Итак, что нужно для перехода от MySQL к PostgreSQL? Это можно сделать за несколько шагов, с использованием pgloader, чтобы помочь в миграции данных.

1. Подготовка MySQL базы

Негобходимо убедиться, что все миграции проведены:

(env)$ python manage.py migrate --settings=settings.production

Теперь можно сделать копию БД и перейти к миграции данных.

2. Установка pgloader

Установить pgloader можно используя средства дистрибутива или скачать из репозитория и собрать из исходных текстов.

3. Создание пользователя и базы в PostgreSQL

В отличие от MySQL, создание новых пользователей и баз данных PostgreSQL обычно происходит в оболочке, а не в клиенте базы данных.

Создадим пользователя и базу данных с таким же именем myproject.

$ createuser --createdb --password myproject
$ createdb --username=myproject myproject

4. Создание схемы

Свяжите проект с базой данных PostgreSQL в настройках, например:

DATABASES = {
    'postgresql': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': get_secret("DATABASE_NAME"), 'USER': get_secret("DATABASE_USER"),
        'PASSWORD': get_secret("DATABASE_PASSWORD"),
    },
}
DATABASES['default'] = DATABASES['postgresql']

В этом функция get_secret () возвращает конфиденциальную информацию из переменных окружения или текстового файла, который не отслеживается при управлении версиями. Но можно использовать любой свой предпочитаемый метод.

Запуск миграции для создания таблиц и связей в новой базе PostgreSQL:

(env)$ python manage.py migrate --settings=settings.local

5. Создание сценария переноса данных

Pgloader использует конфигурационные файлы с настройками, определяющими, как проводить с миграции. Создаем файл конфигурации myproject.load со следующим содержимым:

LOAD DATABASE
    FROM mysql://mysql_username:mysql_password@localhost/mysql_dbname
    INTO postgresql:///myproject
    WITH truncate, data only, disable triggers, preserve index names, include no drop, reset sequences
    ALTER SCHEMA 'mysql_dbname' RENAME TO 'public' ;

6. Запуск миграции данных

$ pgloader myproject.load

Как правило, вылезет большое количество предупреждений о преобразованиях типов. Обычно это можно проигнорировать, потому что сценарий будет стараться угадывать, как конвертировать данные при импорте. Если происходят ошибки, связанные с дублированием данных или таблиц с внешними ключами с потерянными записями, необходимо исправить проблемы в базе данных MySQL, а затем повторить этот процесс. Для этого нужно очистить базу данных MySQL, обновить локальную копию, заново создать базу данных PostgreSQL с помощью команд dropdb и createdb, запустить Django-миграции, чтобы создать схему базы данных, и снова скопировать данные.

7. Адаптация кода

Когда база данных будет успешно перенесена, необходимо запустить тесты проекта Django и исправить все проблемы, связанные с PostgreSQL. Код, написанный с использованием Django ORM, будет работать без проблем. Скорее всего возникнут проблемы с сырым SQL, методом extra() QuerySet и преобразованиями типов.

Как правило, это примерно такие различия:

  • Строковые значения в запросах PostgreSQL всегда оборачиваются 'одиночными кавычками'.
  • PostgreSQL не конвертирует типы при сравнении значений автоматически, как это делает MySQL. Если используется какой-либо сырой SQL-код, нужно выполнить преобразование перед сравнением, например CAST(blog_post.id AS text) = likes_like.object_id или blog_post.id::text = likes_like.object_id. Последний синтаксис с двумя двоеточиями не понимат MySQL, поэтому, если нужно поддерживать обе базы данных, нужно для каждой версии БД свой код.
  • PostgreSQL чувствителен к регистру для сравнения строк, поэтому в фильтрах QuerySet нужно будет использовать *__iexact поиск вместо *__ exact и *__icontains вместо *__contains.
  • При сортировке преобразовывать столбец в нижний регистр с помощью функции Lower ():
    from django.db import models posts = Post.objects.order_by(models.Lower('title'))
  • При использовании *__in убедиться, что тип перечисленных элементов соответствует типу поля модели. Например, может быть модель Like с общим отношением, то есть content_type и object_id, которые вместе создают общий внешний ключ с любым экземпляром модели. Поле object_id обычно имеет тип строки, но идентификаторы связанных моделей могут быть целыми числами, строками или UUID. Если нужно получить понравившиеся сообщения, первичным ключам которых являются целые числа, нужно преобразовать значения object_id в целые числа, прежде чем назначать их для поиска pk__in в фильтре:

    liked_ids = map(
        int,
        Like.objects.filter(
            user=request.user,
            content_type=ContentType.objects.get_for_model(Post)
        ).values("object_id", flat=True)
    )
    liked_posts = Post.objects.filter(pk__in=liked_ids)

8. Повторить процесс на "боевом" сервере

После удачной локальной миграции можно приступать к миграции рабочего проекта.

Вывод

PostgreSQL имеет больше ограничений по работе с ним, чем MySQL, но обеспечивает большую производительность, большую стабильность и лучшее соответствие стандартам. Кроме того, в PostgreSQL существует множество функций, недоступных в MySQL. Если повезет, то можно перевести проект с MySQL на PostgreSQL за один день.

Вернуться на верх