Перейти к содержанию

Импорт параметров при инициализации проекта (loadparameters)

Команда loadparameters — это Django management команда для загрузки параметров из YAML фикстур с автоматической синхронизацией структуры базы данных.

Синтаксис

python manage.py loadparameters [<путь_к_файлу1> <путь_к_файлу2> ...]

Параметры

  • fixtures (опционально) — список путей к YAML-файлам для загрузки
  • --verbosity — уровень детализации вывода (0, 1, 2)
  • --traceback — показывать полный traceback при ошибках

Поведение по умолчанию

  • Если пути к файлам не указаны, команда автоматически ищет файл parameters.yaml в директории fixtures каждого установленного приложения
  • Можно указать один или несколько файлов для загрузки
  • После загрузки выполняется синхронизация структуры параметров: создаются/обновляются таблицы фактов, права доступа и т.д.

Примеры использования

Импорт дефолтного файла из всех приложений

python manage.py loadparameters
Будут загружены все файлы fixtures/parameters.yaml из установленных приложений.

Импорт параметров из конкретного файла

python manage.py loadparameters src/planiqum/apps/test_basics/fixtures/parameters.yaml

Импорт из нескольких файлов

python manage.py loadparameters file1.yaml file2.yaml

Импорт с подробным выводом

python manage.py loadparameters parameters.yaml --verbosity=2

Архитектура команды

Основные компоненты

  1. Загрузка фикстур — использует стандартный механизм Django loaddata
  2. Синхронизация параметров — вызывает parameter.syncdb() для каждого параметра
  3. Создание разрешений — генерирует права доступа для каждого параметра

Алгоритм работы

def handle(self, *args, **kwargs):
    fixtures = kwargs["fixtures"] or ["parameters.yaml"]
    verbosity = kwargs.get('verbosity', 1)
    traceback = kwargs.get('traceback', False)

    # 1. Загрузка фикстур через Django loaddata
    for fixture in fixtures:
        call_command("loaddata", fixture, verbosity=verbosity, traceback=traceback)

    # 2. Синхронизация всех параметров
    for parameter in Parameter.objects.all():
        parameter.syncdb()           # Синхронизация структуры БД
        parameter.get_edit_perm()    # Создание права на редактирование
        parameter.get_view_perm()    # Создание права на просмотр

Что делает команда

1. Загрузка данных

  • Загружает параметры, измерения и меры из YAML-файлов через стандартный механизм фикстур Django
  • Поддерживает все форматы фикстур Django (YAML, JSON, XML)
  • Обрабатывает ошибки валидации и некорректных данных

2. Синхронизация структуры БД

  • Создание fact-таблиц для обычных параметров
  • Создание представлений для вычисляемых параметров
  • Синхронизация индексов для оптимизации запросов
  • Создание таблиц ревизий для мер с поддержкой истории изменений

3. Управление разрешениями

  • view_parameter_{key} — право на просмотр параметра
  • edit_parameter_{key} — право на редактирование параметра
  • Автоматическое создание разрешений для всех загруженных параметров

4. Обработка ошибок

  • CommandError — для несуществующих файлов
  • DeserializationError — для некорректного YAML/JSON
  • IntegrityError — для нарушений ограничений БД
  • SyncConfirmationRequired — для небезопасных операций синхронизации

Формат YAML фикстур

Структура фикстуры

- model: parameters.dimension
  fields:
    key: store
    hierarchy_level: [store_level]
    shortname: Store

- model: parameters.parameter
  fields:
    key: sales_plan
    shortname: Sales Plan
    app_name: sales
    dimensions: [['store']]
    supports_revisions: true
    supports_approvals: false

- model: parameters.measure
  fields:
    key: revenue
    parameter: [sales_plan]
    shortname: Revenue
    type: 0
    is_calculated: false
    supports_uom: true
    supports_currency: true

Обязательные поля

Dimension

  • key — уникальный идентификатор измерения
  • hierarchy_level — список уровней иерархии
  • shortname — отображаемое название

Parameter

  • key — уникальный идентификатор параметра
  • shortname — отображаемое название
  • app_name — название приложения
  • dimensions — список измерений в формате [['dim1'], ['dim2']]

Measure

  • key — уникальный идентификатор меры
  • parameter — ссылка на параметр в формате [parameter_key]
  • shortname — отображаемое название
  • type — тип меры (0 — числовая, 1 — текстовая, 2 — булева)

Интеграция с системой синхронизации

Связь с sync.py

Команда loadparameters тесно интегрирована с системой синхронизации параметров:

# В loadparameters.py
for parameter in Parameter.objects.all():
    parameter.syncdb()  # Вызывает parameter.sync()

# В models.py
def sync(self, force=False):
    from planiqum.core.parameters.libs.sync import ParameterTableSynchronizer, RevisionTableSynchronizer

    # Создание разрешений
    self.get_view_perm()
    self.get_edit_perm()

    # Синхронизация fact-таблицы
    synchronizer = ParameterTableSynchronizer(self)
    synchronizer.sync()

    # Синхронизация таблиц ревизий
    for measure in self.measures.all():
        synchronizer = RevisionTableSynchronizer(measure)
        synchronizer.sync()

Обработка вычисляемых параметров

Для вычисляемых параметров (is_calculated=True) команда: 1. Создаёт представления вместо таблиц 2. Использует SQL из поля calculation_sql 3. Не создаёт таблицы ревизий

Обработка обычных параметров

Для обычных параметров команда: 1. Создаёт fact-таблицы с колонками для измерений и мер 2. Создаёт индексы для оптимизации запросов 3. Создаёт таблицы ревизий для мер с supports_revisions=True

Тестирование

Структура тестов

Все тесты команды loadparameters находятся в директории:

src/planiqum/core/parameters/tests/test_loadparameters/
├── conftest.py                    # Общие фикстуры
├── test_loadparameters_command.py # Основные тесты команды
├── test_fact_table_changes.py     # Тесты изменений fact-таблиц
├── test_metadata_updates.py       # Тесты обновления метаданных
├── test_structure_changes.py      # Тесты структурных изменений
└── fixtures/                      # Тестовые фикстуры
    ├── initial_parameters.yaml
    ├── updated_parameters.yaml
    └── calculated_parameters.yaml

Основные тесты

  1. test_command_with_valid_fixture — загрузка валидной фикстуры
  2. test_command_with_invalid_fixture_file — обработка несуществующих файлов
  3. test_command_with_invalid_yaml — обработка некорректного YAML
  4. test_command_with_invalid_model_data — обработка невалидных данных модели
  5. test_command_with_multiple_fixtures — загрузка нескольких фикстур
  6. test_command_verbosity_levels — работа с разными уровнями verbosity
  7. test_command_handles_existing_objects — обработка существующих объектов
  8. test_command_creates_fact_tables — создание fact-таблиц
  9. test_command_creates_permissions — создание разрешений
  10. test_command_with_calculated_parameters — работа с вычисляемыми параметрами

Запуск тестов

# Все тесты loadparameters
pytest src/planiqum/core/parameters/tests/test_loadparameters/ -v

# Конкретный тест
pytest src/planiqum/core/parameters/tests/test_loadparameters/test_loadparameters_command.py::TestLoadParametersCommand::test_command_with_valid_fixture -v -s

# Тесты с подробным выводом
pytest src/planiqum/core/parameters/tests/test_loadparameters/ -v --tb=short

Новые тесты в PLQM-1161

test_key_change_signal.py

  • Назначение: Тестирование Django signals для переименования таблиц при изменении ключей параметров
  • Ключевые тесты:
  • test_parameter_key_change_renames_fact_table — переименование таблицы фактов при изменении ключа параметра
  • test_parameter_key_change_handles_backup_table — обработка backup-таблиц при изменении ключа
  • test_parameter_key_change_without_existing_table — поведение при отсутствии таблицы фактов
  • test_parameter_key_change_with_existing_new_table — поведение при существовании таблицы с новым именем
  • test_parameter_key_change_logs_operations — логирование операций переименования

test_measure_key_change_signal.py

  • Назначение: Тестирование Django signals для переименования колонок мер и таблиц ревизий
  • Ключевые тесты:
  • test_measure_key_change_renames_column — переименование колонки меры в таблице фактов
  • test_measure_key_change_renames_revision_table — переименование таблицы ревизий
  • test_measure_key_change_without_existing_column — поведение при отсутствии колонки
  • test_measure_key_change_without_existing_revision_table — поведение при отсутствии таблицы ревизий
  • test_measure_key_change_logs_operations — логирование операций переименования

test_admin_unsafe_operations.py

  • Назначение: Тестирование интеграции системы безопасности с Django админкой
  • Ключевые тесты:
  • test_admin_detects_unsafe_measure_deletion — обнаружение небезопасных операций удаления мер
  • test_admin_shows_confirmation_dialog — отображение диалога подтверждения
  • test_admin_executes_confirmed_operations — выполнение подтвержденных операций
  • test_admin_cancels_unsafe_operations — отмена небезопасных операций
  • test_admin_handles_measure_changes — обработка изменений мер в админке
  • test_admin_handles_dimensions_changes — обработка изменений измерений в админке

Запуск новых тестов

# Тесты переименования таблиц при изменении ключей
pytest src/planiqum/core/parameters/tests/test_key_change_signal.py -v

# Тесты переименования колонок и таблиц ревизий
pytest src/planiqum/core/parameters/tests/test_measure_key_change_signal.py -v

# Тесты системы безопасности в админке
pytest src/planiqum/core/parameters/tests/test_admin_unsafe_operations.py -v

# Конкретные тесты с отладочным выводом
pytest src/planiqum/core/parameters/tests/test_key_change_signal.py::test_parameter_key_change_renames_fact_table -v -s
pytest src/planiqum/core/parameters/tests/test_measure_key_change_signal.py::test_measure_key_change_renames_column -v -s
pytest src/planiqum/core/parameters/tests/test_admin_unsafe_operations.py::test_admin_detects_unsafe_measure_deletion -v -s

Отладочный вывод

Тесты содержат подробный отладочный вывод:

=== Тест: Загрузка валидной фикстуры ===
1. Создание тестовой фикстуры...
   Фикстура создана: /tmp/valid.yaml
2. Выполнение команды loadparameters...
   Вывод команды: [детальный вывод Django loaddata]
3. Проверка создания объектов в БД...
   ✓ Параметр создан: sales_plan (ID: 1752)
   ✓ Мера создана: revenue (ID: 4702)
   ✓ Измерение создано: store (ID: 1)
4. Проверка синхронизации структуры БД...
   ✓ Fact-таблица синхронизирована: fact__sales_plan
✅ Тест пройден: Валидная фикстура успешно загружена

Когда использовать

Рекомендуемые сценарии

  1. Инициализация проекта — загрузка базовых параметров при первом запуске
  2. Разработка и тестирование — загрузка тестовых данных
  3. Обновление параметров — загрузка изменений в структуре параметров
  4. Миграция данных — перенос параметров между окружениями

Альтернативы

  • Django админка — для ручного создания/редактирования параметров
  • API — для программного создания параметров
  • Миграции Django — для структурных изменений БД

Ограничения и особенности

Ограничения

  1. Формат фикстур — поддерживаются только YAML, JSON, XML
  2. Порядок загрузки — измерения должны загружаться перед параметрами
  3. Ссылки — параметры в мерах должны существовать до загрузки мер

Особенности

  1. Автоматическая синхронизация — структура БД обновляется автоматически
  2. Обработка ошибок — команда останавливается при первой ошибке
  3. Идемпотентность — повторный запуск с теми же данными безопасен
  4. Транзакционность — все операции выполняются в транзакции

Интеграция с другими командами

initializeproject

Команда loadparameters используется внутри initializeproject:

# В initializeproject.py
call_command('loadparameters', 'parameters.yaml')

sync_parameters

Для принудительной синхронизации без загрузки данных:

python manage.py sync_parameters --all

Отладка и диагностика

Включение отладочного вывода

python manage.py loadparameters parameters.yaml --verbosity=2

Проверка состояния параметров

# В Django shell
from planiqum.core.parameters.models import Parameter

# Список всех параметров
for param in Parameter.objects.all():
    print(f"{param.key}: {param.fact_table_name()}")

# Проверка синхронизации
param = Parameter.objects.get(key='sales_plan')
param.sync(force=True)

Логирование

Для детального анализа процесса включите DEBUG логирование:

import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger('planiqum.core.parameters.libs.sync')
logger.setLevel(logging.DEBUG)

См. также