4.1. Как заставить sqlite-utils писать и чинить базу данных, используя чистый Python-код
Обзор изменений в sqlite-utils 4.1. Научитесь вставлять данные через Python-код, контролировать типы и управлять режимом STRICT.

Если вы работаете с базами данных на уровне скриптов, вы наверняка сталкивались с проблемой: вам нужно не просто загрузить данные из CSV, а выполнить сложную логику — например, сначала обработать каждую строку через функцию, а потом вставить результат. Долгое время для этого приходилось писать обвязку вокруг CLI-утилиты.
В релизе sqlite-utils 4.1 утилита сама добавила поддержку прямого выполнения Python-кода для операций insert и upsert. Это позволяет вам оперировать данными и схемой базы, не выходя из терминала, и самое главное — с сохранением строгой типизации и контроля над индексами.
Зачем это нужно: от файлов к коду
Традиционный рабочий процесс часто выглядит так:
- Написать скрипт, который генерирует данные.
- Сохранить данные в CSV/JSON.
- Запустить CLI-команду
sqlite-utils insert data.csv db.
Если логика вставки данных сложная (например, нужно вычислить full_name из first_name и last_name или применить к полю ZIP код маску), вам приходится писать промежуточный файл.
Новый функционал позволяет вам передать сам Python-код, который генерирует итератор строк, прямо в командную строку. Это кардинально упрощает пайплайны, делая их атомарными и полностью контролируемыми из командной строки.
Встраивание логики: вставка данных через Python-функцию
Самая полезная фича — это возможность использовать опцию --code при вызове insert или upsert. Вместо указания файла, вы передаете блок кода, который должен содержать функцию rows() или любой итератор, который генерирует словари (строки) для вставки.
Это идеальный сценарий для создания тестовых данных или для переноса логики, которая ранее была в отдельном скрипте.
Пример: Вставка данных с помощью кода
Предположим, вам нужно вставить несколько записей в таблицу creatures, где для каждого элемента должна быть своя уникальная логика генерации.
``bash sqlite-utils insert data.db creatures \ --code ' def rows(): # Первый элемент: генерация данных yield {"id": 1, "name": "Cleo"} # Второй элемент: генерация данных yield {"id": 2, "name": "Suna"} ' \ --pk id ``
Что здесь происходит:
- Мы не указываем файл, а используем
--code. - Код внутри кавычек должен содержать функцию
rows(), которая используетyieldдля генерации словарей. sqlite-utilsвыполняет этот код в контексте итератора, а затем вставляет сгенерированные данные.
Полезный лайфхак: Использование `upsert` Если вы используете upsert (обновление/вставка), и вам не нужно указывать первичный ключ (--pk), это тоже стало проще. Утилита теперь может автоматически вывести первичный ключ из существующей схемы, если он там определен.
Контроль схемы: защита от неверных типов данных
SQLite — база данных, которая славится своей гибкостью, но эта гибкость может стать проблемой при работе с данными, требующими строгой интерпретации. Классический пример — ZIP-коды. Они выглядят как числа, но в них могут быть ведущие нули (00123), и если база данных интерпретирует их как INTEGER, вы потеряете эти нули.
Решение — явно указать тип данных.
С помощью новой опции --type вы можете переопределить тип столбца, который sqlite-utils выбрал автоматически.
Сценарий: Вставка данных, где zip_code должен быть TEXT, а не INTEGER.
``bash sqlite-utils insert data.db people \ --type zip_code TEXT \ --code ' def rows(): yield {"name": "Alex", "zip_code": "00123"} ' ``
Это критически важно для любой системы, где данные имеют строгую структуру, но могут быть ошибочно интерпретированы как численные.
Кроме того, появилась удобная команда для управления индексами: table.drop_index(name) или команда sqlite-utils drop-index. Если вам нужно удалить индекс по имени, вам больше не придется вручную писать DROP INDEX name;.
Управление режимом STRICT: когда данные должны быть идеальными
В контексте работы с базами данных, где важна целостность схемы, ключевым понятием является режим STRICT.
По умолчанию, SQLite может быть достаточно "снисходительным" к ошибкам схемы (например, если вы пытаетесь вставить строку в столбец, который должен быть числом). В режиме STRICT база данных требует, чтобы все операции были строго типизированы, что предотвращает многие типы ошибок и повышает надежность.
Раньше переключение режима было сложным процессом, часто требующим полной миграции данных. Теперь sqlite-utils упрощает этот процесс.
Вы можете использовать метод table.transform() или команду sqlite-utils transform с флагами --strict или --no-strict.
Пример: Перевод таблицы в строгий режим
Если вы хотите гарантировать, что ваша таблица users всегда будет работать в строгом режиме:
``bash sqlite-utils transform data.db users --strict ``
Это не просто меняет флаг, это механизм, который, по сути, копирует данные из нестрогого в строгое представление, гарантируя, что данные соответствуют новым правилам.
Продвинутые сценарии: запрос из STDIN
Для построения полностью автоматизированных конвейеров (pipelines) вам может понадобиться запустить команду query, но получить SQL-запрос не из файла, а из конвейера (pipe).
В версии 4.1 вы можете передать SQL-запрос в стандартный ввод (stdin) с помощью флага -.
Пример:
``bash echo "select * from dogs" | sqlite-utils query dogs.db - ``
Вместо того чтобы писать sqlite-utils query dogs.db "select * from dogs", вы передаете сам запрос, что делает скрипты более гибкими и позволяет комбинировать вывод одной утилиты как входные данные для другой.
Подводные камни
- Сложность кода в CLI: Хотя возможность передавать Python-код через
--code— это мощно, помнить синтаксис Python и правильный контекстyieldвнутри кавычек может быть утомительно. Если логика превышает 5-10 строк, лучше вынести ее в отдельный.pyфайл и использовать путь к файлу. - Управление транзакциями: При работе с крупными объемами данных, всегда проверяйте, как
sqlite-utilsобрабатывает транзакции. В сложных пайплайнах лучше использовать явные блоки транзакций, чтобы обеспечить атомарность операций. - Совместимость типов: Несмотря на опцию
--type, если вы вставляете данные из внешнего источника, который может содержать смешанные типы (например, строка с числом), всегда проверяйте, что ваш Python-код предварительно нормализует данные.
Что попробовать дальше
- Скрипт с генерацией данных: Создайте скрипт, который генерирует 100 записей, используя Python-генератор и передает его в
sqlite-utilsчерез--code. - Интеграция с CI/CD: Используйте команду
queryсstdinдля тестирования базы данных: передайте тестовый SQL-запрос, и проверьте, что утилита его корректно выполняет. - Схема-миграции: Настройте рабочий процесс, который сначала запускает
sqlite-utils transform --strict, а уже потом пытается вставить данные. Это гарантирует, что схема готова к приему данных в идеальном состоянии.
Источники
Читайте также
Комментарии
Пока никто не написал. Будьте первым.


