Без ORM. Вдогонку
В предыдущем посте я поделился мыслями насчет преимуществ сырого SQL перед ORM. Забыл привести два следующих аргумента.
Миграции
Напомню, наши миграции – это SQL файлы. Специальный скрипт прогоняет их в особом порядке, оборачивая в транзакции. Преимущество в том, что я могу прогнать их самостоятельно через psql, если вдруг возникнет такая возможность.
Пример типичной миграции:
-- change column types
alter table users
alter column username type text,
alter column username set not null;
Здесь я просто меняю тип поля на text
с varchar
и добавляю ограничение на
обязательное заполнение. Чтобы проверить миграцию, просто запускаю ее вручную:
psql --user foo foo-dbm < resources/migrations/20161107112942-change-username.up.sql
Если у вас база в Докере, то выигрыш в том, что при старте образа запускается
специальный скрипт. Он автоматом прогоняет .sql
-файлы из особой
папки. Достаточно накидать в эту папку файлы миграций, и они выполнятся! Вот как
это делается в compose
-файле:
version: '2'
services:
...
postgres:
image: postgres
volumes:
- <папка с миграциями>:/docker-entrypoint-initdb.d:ro
Сравните теперь с типичной Django-миграцией:
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("migrations", "0001_initial")]
operations = [
migrations.DeleteModel("Tribble"),
migrations.AddField("Author", "rating", models.IntegerField(default=0)),
]
Во-первых, это Питон, не SQL. Чтобы понять, что происходит, нужно замаппить классы и методы на SQL-термы. Поскольку мы редко редактируем миграции и вообще лазим в них, чтение этого кода дается мне с трудом. Не говоря уж о том, что код, сгенерированный машиной, не предназначен для людей.
Далее, этот код может быть запущен только на Питоне и только в особом окружении. Нужна Джанга, вирутальное окружение, верные переменные среды. Просто запустить его я не могу.
По Ричу Хики, SQL-миграция – просто, Джанго-миграция – легко. Я за простоту.
Рекурсивные запросы
В предыдущем проекте я столкнулся с иерархической структурой данных со ссылкой на саму себя. Это было что-то вроде структуры папок. Узел, который ссылается на родительский узел, который… и так далее, до тех пор, пока ссылка на родителя пустая. Это значит, достигнута вершина иерархии.
При этом самые частые операции в коде были перемещение от элемента на самый вверх или на самый низ. Что сделал разработчик? Понятно, лезть в базу поштучно очень дорого, в цепочке может быть 20 узлов. Решил проблему радикально – вынимает из базы ВСЕ узлы, потом стоит иерархический словарь в памяти и перемещается по нему. Это работало, потому что в базе было мало узлов, около тысячи. Ради забавы я написал юнит-тест с 10000 узлами, код подвис секунд на пять.
А ведь достаточно было использовать рекурсивные запросы! Они не только в Постгресе есть, но даже в SQLite! Запрос разбивается на две подчасти. Первая – инициализирующая часть, вторая – накопительная. Пример с Хабра:
WITH RECURSIVE r AS (
SELECT id, parent_id, name, 1 AS level
FROM geo
WHERE id = 4
UNION ALL
SELECT geo.id, geo.parent_id, geo.name, r.level + 1 AS level
FROM geo
JOIN r
ON geo.parent_id = r.id
)
SELECT * FROM r;
id | parent_id | name | level
----+-----------+-----------------+-------
4 | 2 | Европа | 1
5 | 4 | Россия | 2
6 | 4 | Германия | 2
7 | 5 | Москва | 3
8 | 5 | Санкт-Петербург | 3
9 | 6 | Берлин | 3
(6 rows)
В таком запросе молжно получить еще и уровень вложенности, что просто супер для работы с деревьями, например, с комментариями, папками. И все это – без навороченных классов, методов и перегрузок операторов.
Любите SQL – прекрасный, понятный язык запросов к данным!
Нашли ошибку? Выделите мышкой и нажмите Ctrl/⌘+Enter
Igor Shevchenko, 13th Nov 2016, link
А как вы поступаете с миграциями данных? Иногда их довольно тяжело реализовать на SQL. По моему личному опыту, хоть я и предпочитаю использовать RunSQL в тех же джанго-миграциях, периодически приходится применять и RunPython.
Ivan Grishaev, 14th Nov 2016, link , parent
Сейчас у нас нет миграций, которые требуют обработки данных языком программирования. Простые операции типа обработки строк и цифр мы делаем силами Постгреса, просто пишем одноразовые функции на время жизни миграции.