Синхронизация параметров (для разработчиков) ¶
Для разработчиков: Технические детали реализации синхронизации параметров, архитектура классов, логика работы и тестирование.
Введение ¶
Синхронизация параметров в Planiqum — это комплексный механизм автоматического управления структурой таблиц базы данных. Система обеспечивает создание, обновление и удаление таблиц фактов, таблиц ревизий и представлений в соответствии с определением параметров в Django ORM.
См. также: Синхронизация параметров (для администраторов) — описание процесса с точки зрения администратора.
Синхронизация через админку ¶
Автоматическая синхронизация¶
При работе с параметрами в админке Django синхронизация запускается автоматически через скрипт sync_parameters:
ParameterAdmin.response_change()— запускает синхронизацию после изменения параметраParameterAdmin.response_add()— запускает синхронизацию после создания параметраDimensionAdmin.response_change()— запускает синхронизацию после изменения измеренияDimensionAdmin.response_add()— запускает синхронизацию после добавления измеренияMeasureAdmin.response_change()— запускает синхронизацию после изменения мерыMeasureAdmin.response_add()— запускает синхронизацию после добавления меры
Обработка небезопасных операций в админке ¶
Процесс проверки безопасности ¶
Расположение: /src/planiqum/core/parameters/admin.py
Метод: ParameterAdmin.save_formset()
Логика работы:
-
Проверка форм на удаление:
for form in formset.forms: if form.cleaned_data.get('DELETE'): measure = form.instance # Проверяем безопасность удаления check_measure_deletion_safety(measure) -
Обнаружение небезопасной операции:
if unsafe_operation_detected: # Убираем флаг DELETE из формы form.cleaned_data['DELETE'] = False # Устанавливаем флаг для последующей обработки self._unsafe_operation_detected = True -
Временное исключение небезопасных форм:
# Временно исключаем небезопасные формы из обработки safe_forms = [form for form in formset.forms if form not in unsafe_forms] formset.forms = safe_forms # Пересчитываем индексы удаленных форм
Диалог подтверждения ¶
Метод: ParameterAdmin.response_change()
Логика перенаправления:
if self._unsafe_operation_detected:
# Сохраняем детали операции в сессии
request.session['unsafe_operation'] = {
'operation_type': 'delete_measure',
'parameter_id': obj.id,
'measure_id': measure.id,
'measure_key': measure.key,
'details': exception.details
}
# Перенаправляем на диалог подтверждения
return redirect('admin:parameters_confirm_unsafe_operation')
Обработка подтверждения ¶
Метод: ParameterAdmin.confirm_unsafe_operation()
Варианты действий:
-
Подтверждение операции:
if request.POST.get('confirm') == 'yes': # Восстанавливаем флаг DELETE measure.delete() # Запускаем синхронизацию parameter.sync() -
Отмена операции:
if request.POST.get('cancel') or request.POST.get('confirm') == 'no': # Возвращаемся к исходному ChangeView return redirect('admin:parameters_parameter_change', object_id=parameter_id)
Обработка ошибок¶
При автоматической синхронизации система обрабатывает ошибки следующим образом:
- Успешная синхронизация — показывается сообщение об успехе
- Ошибка
SyncConfirmationRequired— пользователь перенаправляется на страницу подтверждения - Ошибка
UnsafeOperationException— пользователь перенаправляется на диалог подтверждения небезопасной операции - Другие ошибки — показывается сообщение об ошибке
Ручной запуск через action¶
Action sync_parameters в ParameterAdmin запускает синхронизацию асинхронно:
def sync_parameters(self, request, queryset):
# Ищем скрипт синхронизации
script = Script.objects.get(
app_name="src.planiqum.core.parameters",
shortname="sync_parameters"
)
# Запускаем асинхронно
task = script.execute(
async_=True,
parameter_ids=parameter_ids,
force=False
)
Страница подтверждения¶
При необходимости принудительной синхронизации пользователь перенаправляется на confirm_table_clear, где:
- Показывается предупреждение о потенциальной потере данных
- Запрашивается подтверждение
- После подтверждения запускается синхронизация с
force=True
Архитектура синхронизации ¶
Основные компоненты ¶
Система синхронизации построена на следующих ключевых классах:
1. ParameterTableSynchronizer ¶
Расположение: /src/planiqum/core/parameters/libs/sync.py
Назначение: Синхронизация таблиц фактов для обычных параметров.
Основные методы:
- sync(force=False) — основная логика синхронизации
- create_table() — создание новой таблицы фактов
- update_table(force=False) — обновление существующей таблицы
- _sync_indexes() — синхронизация индексов
Логика работы: 1. Проверка существования таблицы 2. Сравнение ожидаемой и текущей структуры 3. Создание/обновление/удаление колонок 4. Синхронизация индексов 5. Обработка ошибок и fallback механизмы
2. RevisionTableSynchronizer ¶
Расположение: /src/planiqum/core/parameters/libs/sync.py
Назначение: Синхронизация таблиц ревизий для хранения истории изменений.
Основные методы:
- sync() — создание/удаление таблицы ревизий
- create_table() — создание таблицы с правильной структурой
- _get_expected_indexes() — определение ожидаемых индексов
Особенности:
- Таблицы ревизий создаются только для мер с is_calculated=False
- Структура таблицы фиксирована: id, fact_id, revision_id, value
- Важно: Тип колонки value определяется только при создании таблицы
3. ParameterViewBuilder ¶
Расположение: /src/planiqum/core/parameters/libs/parameter_view_builder.py
Назначение: Создание и управление представлениями для вычисляемых параметров.
Поддерживаемые типы: - Обычные представления (views) — для динамических данных - Материализованные представления (materialized views) — для кэширования
Базовый класс TableSynchronizer ¶
Расположение: /src/planiqum/core/parameters/libs/sync.py
Назначение: Общая логика для всех типов синхронизации таблиц.
Ключевые методы:
- _sync_indexes() — универсальная синхронизация индексов
- _get_existing_indexes() — получение существующих индексов из PostgreSQL
- _create_index() — создание индекса с обработкой ошибок
Логика синхронизации ¶
Процесс синхронизации параметра ¶
# Основной метод синхронизации
def sync(self, force=False):
if self.is_calculated:
# Для вычисляемых параметров создаём представления
self._sync_views()
else:
# Для обычных параметров синхронизируем таблицы
self._sync_tables()
self._sync_revision_tables()
Синхронизация таблиц фактов ¶
1. Анализ структуры ¶
def update_table(self, force=False):
# Получение текущей и ожидаемой структуры
current_columns = self._get_current_structure()
expected_columns = self._get_expected_structure()
# Определение необходимых изменений
to_add = [col for col in expected_columns if col not in current_columns]
to_delete = [col for col in current_columns if col not in expected_columns]
to_alter = self._get_columns_to_alter(current_columns, expected_columns)
2. Обработка изменений ¶
Приоритеты обработки:
1. Generated columns — пересоздаются полностью (DROP + ADD)
2. Колонка id — ⚠️ ВРЕМЕННО ОТКЛЮЧЕНО (2024-08-25)
3. Обычные колонки — ALTER TYPE с fallback на DROP + ADD
3. Механизм подтверждения ¶
# Проверка безопасности изменений
if row_count > 0 and (to_add or to_delete or to_alter) and not force:
raise SyncConfirmationRequired(
f"Требуется подтверждение для изменения структуры заполненной таблицы {table_name}",
details={'row_count': row_count, 'to_add': to_add, 'to_delete': to_delete, 'to_alter': to_alter}
)
Синхронизация таблиц ревизий ¶
Создание таблицы ревизий ¶
def create_table(self):
sql = f"""
CREATE TABLE {self.table_name} (
id BIGSERIAL PRIMARY KEY,
fact_id BIGINT NOT NULL,
revision_id BIGINT NOT NULL,
value {self.measure.sql_type()} NOT NULL
)
"""
execute_sql(sql)
Индексы таблиц ревизий ¶
Ожидаемые индексы:
- Первичный ключ: id (автоматически создаётся)
- Индекс по fact_id: idx_fact_{table_name} (не уникальный)
- Индекс по revision_id: idx_revision_{table_name} (не уникальный)
- Уникальный составной индекс: idx_composite_{table_name} на (fact_id, revision_id)
Синхронизация индексов ¶
Алгоритм синхронизации ¶
def _sync_indexes(self):
existing_indexes = self._get_existing_indexes()
expected_indexes = self._get_expected_indexes()
# Определение необходимых изменений
to_add = [idx for idx in expected_indexes if not self._index_exists(idx, existing_indexes)]
to_remove = [idx for idx in existing_indexes if not self._index_expected(idx, expected_indexes)]
to_rename = self._get_indexes_to_rename(existing_indexes, expected_indexes)
Обработка ошибок ¶
Fallback механизм для уникальных индексов:
try:
execute_sql(create_index_sql)
except IntegrityError:
# При ошибке дублирования используем TRUNCATE
execute_sql(f"TRUNCATE TABLE {table_name}")
execute_sql(create_index_sql)
Особенности реализации ¶
Миграция 0020_add_order_to_measure ¶
Дата: 2025-09-15
Описание: Добавление поля order в модель Measure и обновление choices для calculation_method.
Изменения:
- Добавлено поле order для сортировки мер в админке
- Обновлены choices для поля calculation_method:
- raw_sql — SQL-запрос
- custom_agg — Пользовательская агрегация
- generated_column — Generated колонка PostgreSQL
- Добавлена поддержка PostgreSQL generated columns
Влияние на систему: - Меры теперь можно сортировать в админке Django - Добавлена поддержка высокопроизводительных generated columns - Улучшена производительность вычисляемых мер - Расширены возможности для оптимизации запросов
Совместимость: - Миграция обратно совместима - Существующие меры продолжают работать без изменений - Новые возможности доступны для новых мер
Временное отключение изменения типа колонки id ¶
Статус: ⚠️ ВРЕМЕННО ОТКЛЮЧЕНО (2024-08-25)
Причина: Изменение типа колонки id на больших таблицах может занимать значительное время (~10 минут).
Реализация:
# ВРЕМЕННО: Пропускаем обработку колонки id
if name == 'id':
continue
План восстановления:
1. Создать миграцию для обновления типов id на всех fact-таблицах
2. Применить миграцию в нерабочее время
3. Раскомментировать код в sync.py
4. Восстановить тесты
Обработка вычисляемых мер ¶
Логика: Вычисляемые меры (is_calculated=True) не создают таблицы ревизий.
Причина: Ревизии предназначены для хранения истории изменений физических данных, а не вычисляемых значений.
Fallback механизм для изменения типов ¶
Сценарий: При неудачном ALTER TYPE для обычных колонок.
Реализация:
try:
execute_sql(f"ALTER TABLE {table_name} ALTER COLUMN {col_name} TYPE {target_type}")
except Exception:
if force:
# Fallback: DROP + ADD (может привести к потере данных)
fallback_sql = f"ALTER TABLE {table_name} DROP COLUMN {col_name}; ALTER TABLE {table_name} ADD COLUMN {sql_def}"
execute_sql(fallback_sql)
Тестирование ¶
Структура тестов синхронизации ¶
Расположение: /src/planiqum/core/parameters/tests/
Все тесты, связанные с синхронизацией параметров, организованы в двух основных директориях:
src/planiqum/core/parameters/tests/
├── sync/ # Тесты синхронизации
│ ├── __init__.py # Описание пакета
│ ├── conftest.py # Специфичные фикстуры для синхронизации
│ ├── test_parameter_sync.py # 29 тестов - синхронизация fact-таблиц
│ ├── test_revision_sync.py # 12 тестов - синхронизация ревизий
│ └── test_unsafe_operations.py # 9 тестов - проверки безопасности
└── test_loadparameters/ # Тесты команды loadparameters
├── __init__.py # Описание пакета
├── conftest.py # Общие фикстуры для loadparameters
├── test_loadparameters_command.py # 10 тестов - основная функциональность
├── test_fact_table_changes.py # 8 тестов - изменения fact-таблиц
├── test_metadata_updates.py # 6 тестов - обновление метаданных
├── test_structure_changes.py # 5 тестов - структурные изменения
└── fixtures/ # Тестовые фикстуры
├── initial_parameters.yaml
├── updated_parameters.yaml
└── calculated_parameters.yaml
Основные тесты синхронизации ¶
1. test_parameter_sync.py ¶
Назначение: Тестирование основной функциональности синхронизации параметров.
Ключевые тесты:
- test_initial_table_creation — создание новой таблицы
- test_table_update_with_data — обновление заполненной таблицы
- test_measure_type_change_convertible — изменение типа меры
- test_id_column_type_change_integer_to_bigint — изменение типа колонки id (временно отключено)
- test_generated_column_creation — создание generated columns
- test_generated_column_cycle — проверка циклических зависимостей
- test_view_creation — создание представлений для вычисляемых параметров
2. test_revision_sync.py ¶
Назначение: Тестирование синхронизации таблиц ревизий.
Ключевые тесты:
- test_revision_table_creation_on_parameter_creation — создание при создании параметра
- test_revision_table_measure_type_change_sync — поведение при изменении типа меры
- test_revision_table_index_sync — синхронизация индексов
- test_fact_table_sync_with_data — синхронизация fact-таблиц с данными
3. test_unsafe_operations.py ¶
Назначение: Тестирование системы проверки безопасности операций.
Ключевые тесты:
- test_unsafe_measure_deletion_flow — полный flow небезопасного удаления меры
- test_safe_measure_deletion_flow — безопасное удаление меры без данных
- test_unsafe_measure_deletion_confirmation_flow — подтверждение небезопасной операции
- test_unsafe_measure_deletion_cancellation_flow — отмена небезопасной операции
- test_view_dependency_detection — обнаружение зависимостей в views
- test_trigger_dependency_detection — обнаружение зависимостей в triggers
- test_complex_dependencies_detection — комплексные зависимости
- test_generated_column_dependency_detection — зависимости generated columns (модельный уровень)
- test_database_generated_column_dependency_detection — зависимости generated columns (уровень БД)
- test_calculated_parameter_view_dependency_detection — зависимости в вычисляемых параметрах (views)
- test_unsynced_calculated_parameter_dependency_detection — зависимости в несинхронизированных вычисляемых параметрах
Тесты команды loadparameters ¶
1. test_loadparameters_command.py ¶
Назначение: Тестирование основной функциональности команды loadparameters.
Ключевые тесты:
- test_command_with_valid_fixture — загрузка валидной фикстуры с отладочным выводом
- test_command_with_invalid_fixture_file — обработка несуществующих файлов
- test_command_with_invalid_yaml — обработка некорректного YAML синтаксиса
- test_command_with_invalid_model_data — обработка невалидных данных модели
- test_command_with_multiple_fixtures — загрузка нескольких фикстур за один вызов
- test_command_verbosity_levels — работа с разными уровнями verbosity
- test_command_handles_existing_objects — обработка существующих объектов
- test_command_creates_fact_tables — создание fact-таблиц при загрузке
- test_command_creates_permissions — создание разрешений для параметров
- test_command_with_calculated_parameters — работа с вычисляемыми параметрами
Особенности: - Все тесты содержат подробный отладочный вывод - Детальные описания целей и ожидаемого поведения - Проверка интеграции с системой синхронизации - Тестирование обработки ошибок на разных уровнях
2. test_fact_table_changes.py ¶
Назначение: Тестирование изменений в структуре fact-таблиц при загрузке параметров.
Ключевые тесты:
- test_initial_fact_table_creation — создание новой fact-таблицы
- test_index_creation_and_update — создание и обновление индексов
- test_dimension_changes_affect_table_structure — влияние изменений измерений на структуру
- test_measure_changes_affect_table_structure — влияние изменений мер на структуру
- test_calculated_parameter_creates_view — создание представлений для вычисляемых параметров
- test_calculated_measure_creates_generated_column — создание generated columns
- test_uom_currency_columns_creation — создание колонок UOM и валют
- test_table_structure_validation — валидация структуры таблицы
3. test_metadata_updates.py ¶
Назначение: Тестирование обновления метаданных параметров при загрузке.
Ключевые тесты:
- test_parameter_metadata_update — обновление метаданных параметра
- test_measures_update — обновление мер (добавление/удаление)
- test_dimensions_update — обновление измерений
- test_calculated_parameter_update — обновление вычисляемых параметров
- test_permissions_creation — создание разрешений
- test_backup_parameter_creation — создание backup-параметров
4. test_structure_changes.py ¶
Назначение: Тестирование структурных изменений при загрузке параметров.
Ключевые тесты:
- test_initial_load_creates_structure — создание структуры при первоначальной загрузке
- test_reload_with_different_measures — перезагрузка с другими мерами
- test_reload_with_different_dimensions — перезагрузка с другими измерениями
- test_parameter_type_changes — изменение типа параметра (обычный ↔ вычисляемый)
- test_complex_structure_changes — комплексные структурные изменения
5. 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 — логирование операций переименования
Особенности: - Тестирует автоматическое переименование таблиц через Django signals - Проверяет обработку backup-таблиц - Валидирует логирование операций - Проверяет безопасность операций
6. 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 — логирование операций переименования
Особенности: - Тестирует автоматическое переименование колонок через Django signals - Проверяет переименование таблиц ревизий - Валидирует логирование операций - Проверяет безопасность операций
7. 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 — обработка изменений измерений в админке
Особенности: - Тестирует полный flow от обнаружения до выполнения небезопасных операций - Проверяет интеграцию с Django админкой - Валидирует сериализацию данных в сессии - Проверяет обработку различных типов небезопасных операций
Тестовые фикстуры ¶
Общие фикстуры ¶
Расположение: /src/planiqum/core/parameters/tests/conftest.py
Основные фикстуры:
- parameter_with_revisions — параметр с поддержкой ревизий
- parameter_without_revisions — параметр без поддержки ревизий
- calculated_parameter_with_revisions — вычисляемый параметр
Специфичные фикстуры для синхронизации ¶
Расположение: /src/planiqum/core/parameters/tests/sync/conftest.py
Специальные фикстуры:
- sync_test_parameter — тестовый параметр для синхронизации
- sync_test_measure — тестовая мера для синхронизации
- clean_database — очистка созданных таблиц после тестов
Запуск тестов ¶
Тесты синхронизации¶
# Все тесты синхронизации
pytest src/planiqum/core/parameters/tests/sync/ -v
# Конкретная категория тестов
pytest src/planiqum/core/parameters/tests/sync/test_parameter_sync.py -v
pytest src/planiqum/core/parameters/tests/sync/test_revision_sync.py -v
pytest src/planiqum/core/parameters/tests/sync/test_unsafe_operations.py -v
# Конкретный тест
pytest src/planiqum/core/parameters/tests/sync/test_parameter_sync.py::test_measure_type_change_convertible -v
# Тесты с подробным выводом
pytest src/planiqum/core/parameters/tests/sync/ -v --tb=short
Тесты команды loadparameters¶
# Все тесты loadparameters
pytest src/planiqum/core/parameters/tests/test_loadparameters/ -v
# Основные тесты команды
pytest src/planiqum/core/parameters/tests/test_loadparameters/test_loadparameters_command.py -v
# Тесты изменений fact-таблиц
pytest src/planiqum/core/parameters/tests/test_loadparameters/test_fact_table_changes.py -v
# Тесты обновления метаданных
pytest src/planiqum/core/parameters/tests/test_loadparameters/test_metadata_updates.py -v
# Тесты структурных изменений
pytest src/planiqum/core/parameters/tests/test_loadparameters/test_structure_changes.py -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
Тесты Django signals¶
# Тесты переименования таблиц при изменении ключей
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_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 -v
# Конкретный тест с отладочным выводом
pytest src/planiqum/core/parameters/tests/test_admin_unsafe_operations.py::test_admin_detects_unsafe_measure_deletion -v -s
Все тесты параметров¶
# Все тесты параметров (синхронизация + loadparameters)
pytest src/planiqum/core/parameters/tests/ -v
# Только тесты синхронизации и loadparameters
pytest src/planiqum/core/parameters/tests/sync/ src/planiqum/core/parameters/tests/test_loadparameters/ -v
Покрытие тестами ¶
Общая статистика:
- Parameter Sync: 29 тестов
- Revision Sync: 12 тестов
- Unsafe Operations: 11 тестов
- LoadParameters Command: 10 тестов
- Fact Table Changes: 8 тестов
- Metadata Updates: 6 тестов
- Structure Changes: 5 тестов
- Key Change Signals: 5 тестов
- Measure Key Change Signals: 5 тестов
- Admin Unsafe Operations: 6 тестов
- Всего: 97 тестов
Покрытие функциональности:
Синхронизация параметров¶
- ✅ Создание и обновление fact-таблиц
- ✅ Синхронизация индексов
- ✅ Создание и управление представлениями
- ✅ Generated columns
- ✅ Синхронизация таблиц ревизий
- ✅ Проверки безопасности операций
- ✅ Зависимости в базе данных (foreign keys, indexes, views, triggers)
- ✅ Зависимости в вычисляемых параметрах (views)
- ✅ Зависимости в несинхронизированных вычисляемых параметрах (calculation_sql)
- ✅ UI взаимодействие в админке
Команда loadparameters¶
- ✅ Загрузка валидных фикстур
- ✅ Обработка ошибок (невалидные файлы, YAML, данные)
- ✅ Загрузка нескольких фикстур
- ✅ Работа с разными уровнями verbosity
- ✅ Создание fact-таблиц при загрузке
- ✅ Создание разрешений для параметров
- ✅ Работа с вычисляемыми параметрами
- ✅ Изменения в структуре fact-таблиц
- ✅ Обновление метаданных параметров
- ✅ Структурные изменения при загрузке
Django Signals¶
- ✅ Автоматическое переименование таблиц фактов при изменении ключей параметров
- ✅ Автоматическое переименование колонок мер при изменении ключей мер
- ✅ Автоматическое переименование таблиц ревизий при изменении ключей мер
- ✅ Автоматическое удаление таблиц ревизий при удалении мер
- ✅ Обработка backup-таблиц при изменении ключей параметров
- ✅ Логирование операций переименования
- ✅ Проверка существования таблиц и колонок
- ✅ Безопасность операций в транзакциях
Интеграция с админкой¶
- ✅ Обнаружение небезопасных операций в Django админке
- ✅ Отображение диалогов подтверждения
- ✅ Выполнение подтвержденных операций
- ✅ Отмена небезопасных операций
- ✅ Обработка изменений мер в inline формах
- ✅ Обработка изменений измерений в админке
- ✅ Сериализация данных в сессии
- ✅ Интеграция с системой безопасности
Ключевые моменты для разработчиков ¶
⚠️ Внимание ¶
- Изменение типа колонки
idвременно отключено — не тестируйте эту функциональность - Таблицы ревизий не обновляют структуру — только создают/удаляют/синхронизируют индексы
- Fallback механизм может привести к потере данных — используйте
force=Trueосторожно - Вычисляемые меры не создают таблицы ревизий — это ожидаемое поведение
- Система проверки безопасности работает на двух уровнях — sync.py и admin.py
- Небезопасные операции блокируются до подтверждения — изменения не применяются автоматически
- Проверка зависимостей выполняется комплексно — модельный уровень + уровень БД
🔧 Рекомендации по разработке ¶
- Всегда тестируйте на копиях данных перед применением в продакшене
- Используйте логирование для отладки процесса синхронизации
- Проверяйте совместимость типов при изменении мер
- Тестируйте fallback механизмы для критически важных параметров
- Используйте централизованную функцию парсинга SQL —
_extract_column_references_from_expression() - Тестируйте проверки безопасности — запускайте тесты из
test_unsafe_operations.py - Проверяйте зависимости generated columns — как на модельном, так и на уровне БД
- Используйте организованную структуру тестов — все тесты синхронизации в
tests/sync/
🐛 Отладка синхронизации ¶
Для детального анализа процесса синхронизации включите DEBUG логирование:
import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger('planiqum.core.parameters.libs.sync')
logger.setLevel(logging.DEBUG)
# Теперь при синхронизации будет подробный вывод
parameter.sync()
Отладочный вывод включает: - 📋 Анализ схемы: Сравнение ожидаемых и текущих колонок - 🔍 Детальный анализ: Логика принятия решений для каждой колонки - 📊 План изменений: Список колонок для добавления/удаления/изменения - 🚨 Проверка безопасности: Обоснование требования подтверждения
Пример вывода:
🔍 АНАЛИЗ СТРУКТУРЫ ТАБЛИЦЫ fact_sales
📋 Ожидаемая схема содержит 5 колонок:
- id: BIGSERIAL (generated: False, field_type: id)
- dim_store: INTEGER (generated: False, field_type: dim)
...
🔎 Анализ колонки 'revenue':
📊 Ожидаемая: DOUBLE PRECISION (generated: False, field_type: m)
📊 Текущая: INTEGER (generated: False)
✅ ALTER: Тип колонки изменился (INTEGER → DOUBLE PRECISION)
Система проверки безопасности операций ¶
Архитектура системы безопасности¶
Система проверки безопасности операций построена на двух уровнях:
1. Уровень синхронизации (sync.py) ¶
Расположение: /src/planiqum/core/parameters/libs/sync.py
Назначение: Проверка безопасности операций изменения структуры таблиц.
Основные проверки: - Удаление колонок с данными - Изменение типов колонок с данными - Удаление системных колонок (id, измерения)
Исключение: SyncConfirmationRequired — выбрасывается при обнаружении опасных операций.
2. Уровень админки (admin.py) ¶
Расположение: /src/planiqum/core/parameters/admin.py
Назначение: Проверка безопасности операций удаления мер в Django админке.
Основные проверки: - Удаление мер с данными в fact-таблицах - Комплексная проверка всех зависимостей (параметры, меры, объекты БД) - Автоматическая очистка зависимостей при подтверждении
Исключение: UnsafeOperationException — выбрасывается при обнаружении небезопасных операций.
Комплексная система проверки безопасности удаления мер ¶
Расположение: /src/planiqum/core/parameters/libs/sync_safety_check.py
Основная функция check_measure_deletion_safety(measure) ¶
Назначение: Комплексная проверка безопасности удаления меры с полным анализом всех зависимостей.
Алгоритм работы:
- Сбор всех зависимостей через
collect_all_measure_dependencies(measure) - Проверка наличия данных в fact-таблице
- Анализ безопасности операции на основе собранных данных
- Формирование детального сообщения о рисках
- Выброс исключения
UnsafeOperationExceptionпри небезопасной операции
Структура возвращаемых данных:
{
'is_safe': bool, # Безопасна ли операция
'message': str, # Сообщение для пользователя
'details': {
'has_data': bool, # Есть ли данные в таблице
'data_details': { # Детали о данных
'total_rows': int,
'non_null_rows': int,
'min_value': float,
'max_value': float
},
'dependencies': { # Все найденные зависимости
'calculated_parameters': [...],
'generated_measures': [...],
'database_dependencies': [...],
'has_dependencies': bool,
'total_count': int
},
'total_dependencies': int
}
}
Функция collect_all_measure_dependencies(measure) ¶
Назначение: Сбор всех зависимостей меры без выброса исключений.
Типы собираемых зависимостей:
-
Вычисляемые параметры (
calculated_parameters):# Поиск параметров, использующих меру в calculation_sql for param in Parameter.objects.filter(is_calculated=True): if measure.key in param.calculation_sql: dependencies['calculated_parameters'].append({ 'parameter': param, 'calculation_sql': param.calculation_sql, 'column_references': _extract_column_references_from_expression(param.calculation_sql) }) -
Вычисляемые меры (
generated_measures):# Поиск мер, использующих данную меру в raw_sql for other_measure in Measure.objects.filter(is_calculated=True): if measure.key in other_measure.raw_sql: dependencies['generated_measures'].append({ 'measure': other_measure, 'expression': other_measure.raw_sql, 'column_references': _extract_column_references_from_expression(other_measure.raw_sql) }) -
Объекты базы данных (
database_dependencies): - Views (представления)
- Generated columns (вычисляемые колонки)
- Triggers (триггеры)
- Indexes (индексы)
- Foreign keys (внешние ключи)
Функция _collect_database_dependencies(table_name, column_name) ¶
Назначение: Сбор всех зависимостей колонки на уровне базы данных.
Проверяемые зависимости:
-
Foreign Key Constraints:
SELECT constraint_name, table_name, column_name FROM information_schema.key_column_usage WHERE referenced_table_name = %s AND referenced_column_name = %s -
Indexes:
SELECT indexname, indexdef FROM pg_indexes WHERE tablename = %s AND indexdef LIKE %s -
Generated Columns:
SELECT column_name, generation_expression FROM information_schema.columns WHERE table_name = %s AND is_generated = 'ALWAYS' AND generation_expression LIKE %s -
Views (включая вычисляемые параметры):
SELECT table_name, view_definition FROM information_schema.views WHERE view_definition LIKE %s
Особенность: Система обнаруживает зависимости в views, созданных для вычисляемых параметров (is_calculated=True).
Эти views создаются с именем fact__{parameter_key} и содержат SQL из parameter.calculation_sql.
- Triggers:
SELECT t.trigger_name, t.event_manipulation, t.action_statement FROM information_schema.triggers t WHERE t.event_object_table = %s
Возвращаемая структура:
[
{
'type': 'foreign_key',
'constraint_name': 'fk_name',
'table_name': 'table_name',
'column_name': 'column_name',
'description': 'Описание зависимости'
},
{
'type': 'index',
'index_name': 'idx_name',
'definition': 'CREATE INDEX ...',
'description': 'Описание зависимости'
},
# ... другие типы зависимостей
]
Функция cleanup_measure_dependencies(measure, dependencies) ¶
Назначение: Выполнение полной очистки всех зависимостей меры при подтверждении операции.
Алгоритм очистки:
-
Удаление связанных параметров:
for dep in dependencies['calculated_parameters']: dep['parameter'].delete() -
Удаление связанных мер:
for dep in dependencies['generated_measures']: dep['measure'].delete() -
Очистка объектов базы данных:
for dep in dependencies['database_dependencies']: if dep['type'] == 'view': _cleanup_view(dep) elif dep['type'] == 'generated_column': _cleanup_generated_column(dep) elif dep['type'] == 'trigger': _cleanup_trigger(dep) elif dep['type'] == 'index': _cleanup_index(dep) elif dep['type'] == 'foreign_key': _cleanup_foreign_key(dep)
Возвращаемая структура:
{
'success': bool,
'cleaned_parameters': [...],
'cleaned_measures': [...],
'cleaned_database_objects': [...],
'errors': [...]
}
Централизованный парсинг SQL выражений ¶
Расположение: /src/planiqum/core/parameters/libs/sync_safety_check.py
Функция _extract_column_references_from_expression(expression) ¶
Назначение: Извлечение ссылок на колонки из SQL выражений.
Алгоритм: 1. Фильтрация функций: Исключает стандартные SQL функции (SUM, COUNT, COALESCE, etc.) 2. Фильтрация ключевых слов: Исключает SQL ключевые слова (SELECT, FROM, WHERE, etc.) 3. Фильтрация чисел: Исключает числовые литералы 4. Извлечение идентификаторов: Находит все идентификаторы, которые могут быть колонками
Использование:
- Анализ raw_sql выражений в generated columns
- Проверка зависимостей между мерами
- Валидация SQL выражений
Умная логика безопасности синхронизации ¶
Принципы безопасности¶
Система синхронизации использует умную логику для определения опасности операций:
1️⃣ Удаление колонки id¶
❌ ВСЕГДА ОПАСНО при наличии записей в таблице
Причина: Удаление колонки id нарушает структуру таблицы и может привести к потере данных и нарушению связей.
Логика:
- Если column_name == 'id' и row_count > 0 → требуется подтверждение
- Тип операции: delete_id_column
- Логируется как WARNING
2️⃣ Удаление колонок измерений (dim)¶
❌ ОПАСНО при наличии записей в таблице
✅ БЕЗОПАСНО если таблица пустая
Причина: Колонки измерений содержат ключевую информацию для связи данных с элементами иерархий. Их удаление приводит к потере связности данных.
Логика:
- Использует константу DIM_PREFIX из models
- Если column_name.startswith(DIM_PREFIX) и row_count > 0 → требуется подтверждение
- Тип операции: delete_dimension
- Логируется как WARNING
3️⃣ Удаление колонок мер (m)¶
✅ БЕЗОПАСНО если колонка содержит только NULL значения
❌ ОПАСНО если колонка содержит реальные данные
Причина: Пустые колонки мер можно удалять безопасно, но колонки с данными содержат важную бизнес-информацию.
Логика:
- Использует Measure.is_fact_field_name(column_name) для определения типа
- Проверяется наличие не-NULL значений в колонке
- Если есть данные → требуется подтверждение (delete_measure_with_data)
- Если только NULL → удаление разрешено без подтверждения
- Опасные операции логируются как WARNING
4️⃣ Удаление других колонок¶
✅ БЕЗОПАСНО если колонка содержит только NULL значения или таблица пустая
❌ ОПАСНО если колонка содержит данные
Причина: Системные колонки и колонки неизвестного типа требуют осторожности при наличии данных.
Логика:
- Если row_count > 0 и колонка содержит данные → требуется подтверждение
- Тип операции: delete_column_with_data
- Опасные операции логируются как WARNING
5️⃣ Изменение типа колонок¶
✅ БЕЗОПАСНО если колонка содержит только NULL значения
❌ ОПАСНО если колонка содержит данные
Причина: Изменение типа колонки с данными может привести к потере или некорректному преобразованию данных.
Логика:
- Проверяется наличие не-NULL значений в колонке
- Если есть данные → требуется подтверждение (alter_column_with_data)
- Если только NULL → изменение разрешено без подтверждения
- Опасные операции логируются как WARNING
Важные изменения в логике¶
Оптимизация проверок безопасности¶
- ✅ Проверки выполняются только для заполненных таблиц (
row_count > 0) - ✅ Пустые таблицы: Все операции считаются безопасными
- ✅ Добавление колонок: Больше не считается опасной операцией
Логирование опасных операций¶
- ⚠️ WARNING-логи: Все обнаруженные опасные операции логируются как WARNING
- 📝 Детальная информация: Включает тип операции, имя колонки и количество записей
- 🔍 DEBUG-логи: Безопасные операции логируются как DEBUG
Примеры работы логики¶
# Пример 1: Безопасное удаление пустой меры
sync = ParameterTableSynchronizer(parameter)
try:
sync.update_table(force=False) # Пройдет без подтверждения
print("✅ Пустая мера удалена безопасно")
except SyncConfirmationRequired:
print("❌ Неожиданно: требуется подтверждение")
# Пример 2: Опасное удаление измерения
try:
sync.update_table(force=False) # Выбросит исключение
except SyncConfirmationRequired as e:
dangerous_ops = e.details['dangerous_operations']
for op in dangerous_ops:
if op['type'] == 'delete_dimension':
print(f"⚠️ Опасно: удаление {op['column']}")
# Пример 3: Принудительное выполнение
sync.update_table(force=True) # Пропустит все проверки безопасности
Отладочные сообщения безопасности¶
В DEBUG-логах появляются специальные сообщения:
🔍 АНАЛИЗ БЕЗОПАСНОСТИ: Таблица содержит 1096 записей
⚠️ ОПАСНАЯ ОПЕРАЦИЯ: Удаление колонки id 'id' в таблице с 1096 записями
⚠️ ОПАСНАЯ ОПЕРАЦИЯ: Удаление колонки измерения 'dim_product__brand' в таблице с 1096 записями
⚠️ ОПАСНАЯ ОПЕРАЦИЯ: Удаление колонки меры с данными 'm_sales__qty' в таблице с 1096 записями
⚠️ ОПАСНАЯ ОПЕРАЦИЯ: Изменение типа колонки с данными 'm_revenue' в таблице с 1096 записями
✅ БЕЗОПАСНОСТЬ: Таблица пустая (0 записей) - все операции безопасны
🚨 ПРОВЕРКА БЕЗОПАСНОСТИ:
Есть изменения: True
Таблица заполнена: True
Force режим: False
Требуется подтверждение: True
Опасных операций найдено: 4
⚠️ delete_id_column: id - Удаление колонки id может привести к потере данных и нарушению структуры таблицы
⚠️ delete_dimension: dim_product__brand - Удаление колонки измерения может привести к потере данных
⚠️ delete_measure_with_data: m_sales__qty - Удаление колонки меры с данными приведет к потере данных
⚠️ alter_column_with_data: m_revenue - Изменение типа колонки с данными может привести к потере данных
Структура исключения SyncConfirmationRequired¶
try:
sync.update_table(force=False)
except SyncConfirmationRequired as e:
print(f"Сообщение: {e}")
details = e.details
print(f"Строк в таблице: {details['row_count']}")
print(f"Колонок к добавлению: {len(details['to_add'])}")
print(f"Колонок к удалению: {len(details['to_delete'])}")
print(f"Колонок к изменению: {len(details['to_alter'])}")
# Анализ опасных операций
for op in details['dangerous_operations']:
print(f"Тип: {op['type']}")
print(f"Колонка: {op.get('column', op.get('columns'))}")
print(f"Причина: {op['reason']}")
# Полный отчет для пользователя
print("Отчет пользователю:")
print(details['user_report'])
📋 Статус реализации ¶
✅ Завершенные задачи¶
- Система проверки безопасности операций:
- ✅ Реализована комплексная проверка безопасности удаления мер
- ✅ Добавлена проверка зависимостей generated columns (модельный уровень)
- ✅ Добавлена проверка зависимостей в базе данных (views, triggers, indexes)
- ✅ Добавлена проверка зависимостей в вычисляемых параметрах (views)
- ✅ Добавлена проверка зависимостей в несинхронизированных вычисляемых параметрах (calculation_sql)
- ✅ Централизован парсинг SQL выражений
-
✅ Интегрирована система безопасности в Django админку
-
Организация тестов:
- ✅ Все тесты синхронизации вынесены в отдельную директорию
tests/sync/ - ✅ Созданы специфичные фикстуры для тестов синхронизации
- ✅ Добавлены comprehensive тесты для системы безопасности
-
✅ Все 56 тестов проходят успешно
-
Документация:
- ✅ Обновлена техническая документация с детальным описанием системы безопасности
- ✅ Добавлены примеры кода и SQL запросов
- ✅ Описаны все проверки и их логика работы
🔄 Следующие шаги для реализации¶
- Восстановление изменения типа колонки
id: - Создать миграцию для обновления типов на всех таблицах
- Раскомментировать код в
sync.py -
Обновить тесты
-
Улучшение синхронизации таблиц ревизий:
- Добавить поддержку изменения типа колонки
value - Реализовать миграцию структуры существующих таблиц
-
Добавить валидацию совместимости типов
-
Оптимизация производительности:
- Кэширование метаданных таблиц
- Пакетная обработка изменений
-
Асинхронная синхронизация для больших таблиц
-
Расширение системы безопасности:
- Добавить проверки для других типов небезопасных операций
- Реализовать проверки для удаления измерений
- Добавить валидацию SQL выражений в generated columns
См. также ¶
- Синхронизация параметров (для администраторов) — описание процесса с точки зрения администратора
- Версионирование данных параметров — создание backup-параметров, сохранение и восстановление версий
- Основные концепции — базовые принципы работы с параметрами
- Вычисляемые меры — работа с вычисляемыми мерами и generated columns
- Иерархии: синхронизация — аналогичный механизм для иерархий
- Импорт параметров — автоматическая синхронизация при загрузке
- Система безопасности операций — подтверждение опасных операций
Интеграция с Django админкой ¶
Обработка небезопасных операций в админке¶
Расположение: /src/planiqum/core/parameters/admin.py
Метод save_formset() ¶
Назначение: Перехват операций удаления мер в Django админке с проверкой безопасности.
Алгоритм работы:
-
Проверка форм на удаление:
for inline_form in formset.forms: if inline_form.cleaned_data.get('DELETE', False): if isinstance(inline_form.instance, Measure): check_measure_deletion_safety(measure) -
Обработка небезопасных операций:
except UnsafeOperationException as e: # Преобразование в сериализуемый формат serializable_dependencies = {...} request.session['unsafe_operation'] = { 'type': 'delete_measure', 'measure_id': measure.id, 'measure_key': measure.key, 'dependencies': serializable_dependencies } # Убираем флаг DELETE inline_form.cleaned_data['DELETE'] = False -
Сериализация данных для сессии:
- Объекты Django преобразуются в сериализуемые структуры
- Сохраняются только ID и текстовые поля
- Предотвращается ошибка
Object of type Measure is not JSON serializable
Метод confirm_unsafe_operation() ¶
Назначение: Обработка подтверждения небезопасных операций пользователем.
Алгоритм работы:
-
Получение данных из сессии:
unsafe_operation = request.session.get('unsafe_operation') -
Выполнение очистки при подтверждении:
if request.POST.get('confirm') == 'yes': # Сбор зависимостей заново dependencies = collect_all_measure_dependencies(measure) # Выполнение очистки cleanup_result = cleanup_measure_dependencies(measure, dependencies) # Удаление меры measure.delete() # Синхронизация параметра parameter.sync(force=True) -
Формирование сообщений пользователю:
if cleanup_result['success']: messages.success(request, f"Мера удалена и параметр синхронизирован") else: messages.warning(request, f"Мера удалена, но при очистке возникли ошибки")
Шаблон подтверждения ¶
Расположение: /src/planiqum/templates/admin/confirm_unsafe_operation.html
Функциональность: - Отображение всех найденных зависимостей - Детальная информация о рисках - Предупреждения о последствиях операции - Кнопки подтверждения/отмены
Отображаемые данные: - Статистика данных в таблице - Список вычисляемых параметров - Список вычисляемых мер - Список объектов базы данных - Сводка по количеству объектов для удаления
Тестирование системы безопасности ¶
Структура тестов¶
Расположение: /src/planiqum/core/parameters/tests/sync/test_unsafe_operations.py
Основные тесты ¶
test_unsafe_measure_deletion_flow— проверка обнаружения небезопасных операцийtest_safe_measure_deletion_flow— проверка безопасных операцийtest_unsafe_measure_deletion_confirmation_flow— проверка подтверждения операцийtest_unsafe_measure_deletion_cancellation_flow— проверка отмены операций
Тесты обнаружения зависимостей ¶
test_view_dependency_detection— обнаружение зависимостей в viewstest_trigger_dependency_detection— обнаружение зависимостей в triggerstest_complex_dependencies_detection— комплексные зависимостиtest_generated_column_dependency_detection— зависимости в generated columnstest_database_generated_column_dependency_detection— зависимости на уровне БДtest_calculated_parameter_view_dependency_detection— зависимости в вычисляемых параметрахtest_unsynced_calculated_parameter_dependency_detection— несинхронизированные параметры
Тесты функциональности ¶
test_comprehensive_dependency_collection— сбор всех зависимостейtest_cleanup_measure_dependencies— очистка зависимостейtest_admin_integration_comprehensive_cleanup— интеграция с админкойtest_session_serialization_fix— сериализация данных в сессии
Запуск тестов¶
# Все тесты безопасности
pytest src/planiqum/core/parameters/tests/sync/test_unsafe_operations.py -v
# Конкретный тест
pytest src/planiqum/core/parameters/tests/sync/test_unsafe_operations.py::test_comprehensive_dependency_collection -v -s
# Все тесты синхронизации
pytest src/planiqum/core/parameters/tests/sync/ -v
Отладочный вывод тестов¶
Тесты выводят подробную информацию о процессе:
🧪 ТЕСТ: Комплексная очистка зависимостей
- Параметр: test_param_unsafe
- Мера: test_measure_unsafe
- Создана таблица fact__test_param_unsafe с 3 строками данных
- Создан и синхронизирован вычисляемый параметр calculated_sales
- Создана вычисляемая мера generated_measure
- Создан generated column calculated_value
- Создан индекс на колонку m_test_measure_unsafe
- ✅ Все объекты созданы и существуют
- ✅ Собрано зависимостей: 5
- ✅ Очистка завершена: True
- ✅ Удалено параметров: 1
- ✅ Удалено мер: 1
- ✅ Удалено объектов БД: 3
- ✅ Все объекты успешно удалены
✅ ТЕСТ ПРОШЕЛ: Комплексная очистка зависимостей работает корректно
Django Signals для автоматической синхронизации ¶
Архитектура автоматической синхронизации¶
Система использует Django signals для автоматической синхронизации при изменениях параметров и мер. Это обеспечивает целостность данных и автоматическое управление структурой таблиц без ручного вмешательства.
Расположение: /src/planiqum/core/parameters/signals.py
Основные сигналы ¶
1. post_delete сигнал для мер ¶
Назначение: Автоматическое удаление таблиц ревизий при удалении мер.
Функция: cleanup_measure_revision_table(sender, instance, **kwargs)
Алгоритм работы:
1. Получает сигнал после удаления меры из БД
2. Проверяет существование таблицы ревизий для меры
3. Выполняет DROP TABLE IF EXISTS для надежности
4. Логирует операцию удаления
Пример использования:
# При удалении меры через админку
measure.delete() # Автоматически срабатывает сигнал и удаляет таблицу ревизий
2. pre_save сигнал для параметров ¶
Назначение: Переименование таблиц фактов при изменении ключа параметра.
Функция: rename_fact_table_on_key_change(sender, instance, **kwargs)
Алгоритм работы:
1. Проверяет, изменился ли ключ параметра
2. Проверяет существование таблицы фактов для старого ключа
3. Выполняет ALTER TABLE ... RENAME TO ... для основной таблицы
4. Обрабатывает backup-таблицы (переименование или удаление)
5. Логирует все операции
Обработка backup-таблиц: - Если параметр поддерживает бэкапирование → переименовывает backup-таблицу - Если не поддерживает → удаляет backup-таблицу
См. также: Версионирование данных параметров — подробное описание создания backup-параметров и работы с версиями
3. pre_save сигнал для мер ¶
Назначение: Переименование колонок мер и таблиц ревизий при изменении ключа меры.
Функция: rename_measure_column_and_revision_table_on_key_change(sender, instance, **kwargs)
Алгоритм работы:
1. Проверяет, изменился ли ключ меры
2. Получает параметр и имя таблицы фактов
3. Проверяет существование колонки меры в таблице фактов
4. Выполняет ALTER TABLE ... RENAME COLUMN ... для колонки меры
5. Проверяет существование таблицы ревизий
6. Выполняет ALTER TABLE ... RENAME TO ... для таблицы ревизий
Разделение ответственности ¶
Синхронизация fact-таблиц → parameter.sync() (в админке)
- Создание/обновление структуры таблиц
- Синхронизация индексов
- Обработка generated_columns
Удаление таблиц ревизий → Django signals (автоматически) - Автоматическое удаление после удаления меры - Не требует ручного вмешательства
Переименование таблиц → Django signals (автоматически) - Переименование при изменении ключей - Обработка backup-таблиц - Сохранение целостности данных
Безопасность операций ¶
Транзакционность: - Все операции выполняются в рамках существующей транзакции - При ошибке переименования транзакция откатывается - Не нарушает целостность данных
Обработка ошибок: - Сигналы не поднимают исключения, чтобы не нарушить основной процесс - Все ошибки логируются для отладки - Система продолжает работу даже при сбоях в сигналах
Проверки существования: - Проверка существования таблиц перед переименованием - Проверка существования колонок перед переименованием - Предотвращение конфликтов имен
Тестирование сигналов ¶
Расположение тестов:
- test_key_change_signal.py — тесты переименования таблиц при изменении ключей
- test_measure_key_change_signal.py — тесты переименования колонок и таблиц ревизий
Ключевые тесты:
- test_parameter_key_change_renames_fact_table — переименование таблицы фактов
- test_parameter_key_change_handles_backup_table — обработка backup-таблиц
- test_measure_key_change_renames_column — переименование колонки меры
- test_measure_key_change_renames_revision_table — переименование таблицы ревизий
Запуск тестов:
# Тесты переименования таблиц
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
Ограничения текущей реализации ¶
Отложенная функциональность: - Проверка и обновление всех ссылок на переименованные таблицы в системе - Обновление конфигураций, скриптов и других компонентов - Уведомление зависимых систем об изменении имен таблиц
Планы развития: - Интеграция с системой уведомлений - Автоматическое обновление зависимых объектов - Расширенная проверка целостности данных
Заключение ¶
Синхронизация параметров — это сложный механизм, требующий глубокого понимания архитектуры базы данных и Django ORM. Разработчикам следует внимательно изучать существующие тесты и логику обработки ошибок при внесении изменений.
Ключевые принципы разработки:
- Всегда тестируйте проверки безопасности — запускайте тесты из
test_unsafe_operations.py - Используйте комплексный подход — проверяйте зависимости как на модельном, так и на уровне БД
- Тестируйте сериализацию — убедитесь, что данные корректно сохраняются в сессии
- Проверяйте интеграцию с админкой — тестируйте полный flow от обнаружения до очистки
- Используйте централизованные функции —
_extract_column_references_from_expression()для парсинга SQL
Важно: Все изменения в механизме синхронизации должны сопровождаться соответствующими тестами и документацией для администраторов.