Два скрипта для работы со справочниками через API фасада: снять выгрузку в CSV и залить подготовленные CSV обратно. Только стандартная библиотека Python 3.8+, никаких зависимостей — можно положить на сервер и запускать как есть.
nsi_export.py— выгрузка. Ничего не изменяет, безопасен для боевого стенда.nsi_import.py— загрузка. Без--confirmничего не отправляет.
cp config.example.json config.json # вписать адреса стендов
python3 nsi_export.py # спросит стенд, выгрузит все справочникиВыгрузка ляжет в dumps/<стенд>/<ГГГГ-ММ-ДД_ЧЧ-ММ-СС>/.
config.json рядом со скриптами:
{
"stands": {
"test": {"base": "http://<адрес>", "login": "", "password": ""},
"prod": {"base": "http://<адрес>", "login": "", "password": ""}
}
}Логин и пароль можно не хранить в файле — оставь пустыми, и скрипт спросит их
в консоли при запуске (ввод скрытый, никуда не сохраняется). Либо задай
NSI_BASE / NSI_LOGIN / NSI_PASSWORD в переменных окружения.
config.json закрыт .gitignore — в репозиторий не попадёт.
python3 nsi_export.py # спросит стенд, все справочники
python3 nsi_export.py --stand prod # без вопроса
python3 nsi_export.py --stand test --list # что есть на стенде
python3 nsi_export.py --stand test nsi-base_type # точечно, по кодам
python3 nsi_export.py --stand prod --targets # коды из targets.txt
python3 nsi_export.py --stand prod --summary # + сводка по каждому справочникуСтенд не указан и терминал есть — спросит. Терминала нет (пайп, планировщик) —
остановится и потребует --stand: угадывать стенд слишком дорого.
python3 nsi_import.py --stand test --dir ./to_upload # посмотреть
python3 nsi_import.py --stand test --dir ./to_upload --confirm # залить
python3 nsi_import.py --stand test --file a.csv b.csv --confirm # выборочноСправочник определяется по имени файла — последний фрагмент вида nsi-<код>:
| Имя файла | Справочник | Порядок |
|---|---|---|
nsi-base_type.csv |
nsi-base_type |
по алфавиту |
1_nsi-base_type.csv |
nsi-base_type |
первым |
nsi-base_type_2026-08-21.csv |
nsi-base_type |
по алфавиту |
Числовой префикс задаёт порядок загрузки. Файлы без кода в имени пропускаются с предупреждением.
Несколько особенностей, на которых легко потерять день.
Счётчику в ответе верить нельзя. {"createdAmount": 0, "updatedAmount": 202, "errors": []} выглядит как успех, а означает, что импорт отработал обновлением
вместо вставки — обычно потому, что у новых записей заполнено поле id.
У новых записей id должен быть пустым. Скрипт после каждого файла сам
выгружает справочник обратно и сверяет число записей.
Импорт применяется частично. Ответ с непустым errors выглядит как полный
отказ, но это не так: обновления существующих записей проходят, отбиваются
только вставки. После ошибки нельзя считать, что ничего не произошло — надо
снять выгрузку и посмотреть, что уже применилось. И нельзя вслепую
перезапускать тот же файл.
Порядок загрузки важен. Система проверяет ссылки между справочниками в обе стороны. Если запись A ссылается на запись B, которой ещё нет, импорт отобьётся. При круговой зависимости помогает загрузка в два прохода: сначала записи с пустыми ссылками, потом — заполнение ссылок.
Заголовок CSV должен побайтово совпадать с выгрузкой. Не «примерно те же колонки», а точно те же и в том же порядке. Кодировка UTF-8 BOM, переводы строк CRLF. Надёжный способ собрать файл на загрузку — взять выгрузку и править её.
Формат дат отличается между полями. Служебные (start_date, end_date)
обычно dd.MM.yyyy, прикладные — ISO. При этом тип колонки бывает date или
datetime, и это различается между справочниками: значение с временем в колонку
типа date не пройдёт.
В каждую папку выгрузки кладётся _STAND.txt: с какого стенда сняты данные,
когда, и сколько записей в каждом справочнике.
nsi_import.py читает этот файл и отказывается грузить, если папка снята
не с того стенда, куда её собираются заливать. Папку можно переименовать или
скопировать не туда — маркер внутри остаётся.
- Загрузка идёт последовательно, по одному файлу; отката нет.
- Проверка после загрузки — только по числу записей. Содержимое сверять отдельной выгрузкой.
- Скрипты не создают справочники: CSV наполняет существующий, структуру заводит бэкенд.