minor: план переделки работы с картинками! Через сигналы!
This commit is contained in:
@@ -0,0 +1,445 @@
|
|||||||
|
# План миграции: TbImage → ImageMetadata (Filer Extension)
|
||||||
|
|
||||||
|
**Дата:** 2026-07-07
|
||||||
|
**Время:** 21:09
|
||||||
|
**Автор:** Copilot CLI
|
||||||
|
**Статус:** В планировании
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Обзор
|
||||||
|
|
||||||
|
Переконфигурирование архитектуры работы с изображениями:
|
||||||
|
- **Текущее состояние:** Модели ссылаются на TbImage, которая ссылается на FilerImageField
|
||||||
|
- **Целевое состояние:** Модели ссылаются прямо на filer.Image, дополнительные метаданные хранятся в переименованной TbImage (→ ImageMetadata)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Фаза 1: Подготовка и анализ
|
||||||
|
|
||||||
|
### Задача 1.1: Аудит использования TbImage
|
||||||
|
- [ ] Найти все FK на TbImage в моделях
|
||||||
|
- [ ] Найти все M2M связи на TbImage (см. `TbOffer.k_offer_to_image`)
|
||||||
|
- [ ] Найти все места в админке, которые работают с TbImage
|
||||||
|
- [ ] Найти все места в парсерах/сигналах, которые создают/обновляют TbImage
|
||||||
|
- [ ] Документировать каждое найденное место
|
||||||
|
|
||||||
|
### Задача 1.2: Анализ filer.Image структуры
|
||||||
|
- [ ] Проверить какие даты есть в filer.Image модели (`modified_at`, `uploaded_at` или их эквиваленты)
|
||||||
|
- [ ] Проверить есть ли `related_name` у FilerImageField в TbImage или он будет auto-generated
|
||||||
|
- [ ] Убедиться что filer работает с сигналами при изменении файла
|
||||||
|
|
||||||
|
**Результат:** Составить полный список всех мест, требующих изменения
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Фаза 2: Модификация моделей
|
||||||
|
|
||||||
|
### Задача 2.1: Переименование и переструктурирование TbImage
|
||||||
|
- [ ] Переименовать класс TbImage → ImageMetadata
|
||||||
|
- [ ] Добавить/проверить `related_name='metadata'` на FilerImageField
|
||||||
|
```python
|
||||||
|
image = FilerImageField(
|
||||||
|
...,
|
||||||
|
related_name='metadata' # ← для доступа: filer_image.metadata
|
||||||
|
)
|
||||||
|
```
|
||||||
|
- [ ] **УДАЛИТЬ** поля из ImageMetadata:
|
||||||
|
- `t_img_created` (используем filer.Image.uploaded_at или эквивалент)
|
||||||
|
- `t_img_updated` (синхронизируем с filer.Image.modified_at через сигнал)
|
||||||
|
- [ ] Обновить `Meta.verbose_name` на "Метаданные изображения"
|
||||||
|
- [ ] Обновить `Meta.ordering` (убрать временные поля если они там были)
|
||||||
|
|
||||||
|
### Задача 2.2: Обновление FK на изображения во всех моделях
|
||||||
|
- [ ] TbArticle: `k_article_to_image` — ForeignKey(TbImage) → ForeignKey(filer.Image)
|
||||||
|
- [ ] **TbItem: добавить `k_item_to_image` — ForeignKey(filer.Image, blank=True, null=True)**
|
||||||
|
- Обложка альбома/релиза (всегда `l_img_reality = 'abstract'`)
|
||||||
|
- Дефолтное значение при создании ImageMetadata: `l_img_reality = 'abstract'`
|
||||||
|
- [ ] TbOffer: M2M `k_offer_to_image` — на filer.Image вместо TbImage
|
||||||
|
- Может переиспользовать картинку из TbItem или иметь свою
|
||||||
|
- В админке: `l_img_reality` может быть `'real'` (реальная фото товара) или `'abstract'` (из каталога)
|
||||||
|
- При создании через админку: дефолт `l_img_reality = 'real'` (человек загружает реальные фото)
|
||||||
|
- При создании через парсер: `l_img_reality = 'abstract'` (парсер берет с Discogs и т.д.)
|
||||||
|
- [ ] [Проверить другие модели] — все остальные модели с FK на картинки
|
||||||
|
- [ ] Для каждого изменения:
|
||||||
|
- Обновить саму модель
|
||||||
|
- Создать Django миграцию
|
||||||
|
- Обновить админку (инлайны)
|
||||||
|
|
||||||
|
### Задача 2.3: Создание миграции данных
|
||||||
|
- [ ] Миграция для переименования таблицы (TbImage → ImageMetadata или оставить таблицу, но переименовать модель?)
|
||||||
|
- [ ] Миграция для удаления полей `t_img_created`, `t_img_updated` из ImageMetadata
|
||||||
|
- [ ] Обработка сирот (ImageMetadata без соответствующего filer.Image) — удалить или связать
|
||||||
|
|
||||||
|
**Важно:** Порядок миграций критичен! Сначала добавляем новые FK/M2M, потом удаляем старые.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Фаза 3: Сигналы и автоматизация
|
||||||
|
|
||||||
|
### Задача 3.1: Signal на создание filer.Image → auto-create ImageMetadata
|
||||||
|
```python
|
||||||
|
# В apps.py
|
||||||
|
@receiver(post_save, sender=filer.models.Image)
|
||||||
|
def auto_create_image_metadata(sender, instance, created, **kwargs):
|
||||||
|
"""При создании filer.Image автоматически создавать ImageMetadata с дефолтами"""
|
||||||
|
if created:
|
||||||
|
ImageMetadata.objects.get_or_create(
|
||||||
|
image=instance,
|
||||||
|
defaults={
|
||||||
|
'i_img_sort': 0,
|
||||||
|
'l_img_source': 'manual', # или 'parser' если известно
|
||||||
|
'l_img_reality': 'abstract',
|
||||||
|
'f_img_confidence_score': 10.0,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Задача 3.2: Обратные сигналы — синхронизация дат
|
||||||
|
```python
|
||||||
|
# В apps.py
|
||||||
|
@receiver(post_save, sender=ImageMetadata)
|
||||||
|
def sync_image_modification_date(sender, instance, **kwargs):
|
||||||
|
"""При обновлении ImageMetadata обновлять modified_at в filer.Image"""
|
||||||
|
# Обновляем filer.Image.modified_at = now()
|
||||||
|
# Нужно понять как именно это сделать в filer (direct update без повторного save())
|
||||||
|
```
|
||||||
|
|
||||||
|
**Важно:** Нужно избежать циклических сохранений (Image → Metadata → Image → ...)
|
||||||
|
|
||||||
|
### Задача 3.3: Сигнал на удаление
|
||||||
|
- [ ] При удалении ImageMetadata → не удаляем filer.Image (метаданные удаляются, файл остаётся)
|
||||||
|
- [ ] При удалении filer.Image → каскадно удаляем ImageMetadata (on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Фаза 4: Админка и UI
|
||||||
|
|
||||||
|
### Задача 4.1: Создание ImageMetadataAdminMixin
|
||||||
|
```python
|
||||||
|
class ImageMetadataAdminMixin:
|
||||||
|
"""Миксин для админок, содержащих связь с filer.Image"""
|
||||||
|
# Автоматически добавлять инлайн ImageMetadata при наличии FK на Image
|
||||||
|
```
|
||||||
|
|
||||||
|
### Задача 4.2: Создание ImageMetadataInline
|
||||||
|
- [ ] StackedInline или TabularInline?
|
||||||
|
- [ ] Какие поля показывать: `i_img_sort`, `l_img_source`, `l_img_reality`, `f_img_confidence_score`
|
||||||
|
- [ ] Read-only поля? Или всё редактируемо?
|
||||||
|
|
||||||
|
### Задача 4.3: Обновление админок моделей
|
||||||
|
- [ ] TbArticleAdmin — добавить ImageMetadataInline
|
||||||
|
- [ ] **TbItemAdmin — добавить ImageMetadataInline для обложки (cover image)**
|
||||||
|
- Поле `k_item_to_image` — ForeignKey с инлайном ImageMetadata
|
||||||
|
- ImageMetadata: `l_img_reality` должно быть read-only = `'abstract'`
|
||||||
|
- [ ] **TbOfferAdmin — добавить связь к изображениям с множественным выбором**
|
||||||
|
- M2M к filer.Image с инлайном ImageMetadata
|
||||||
|
- ImageMetadata: `l_img_reality` редактируемо (админ может менять на `'real'`)
|
||||||
|
- [ ] [Все остальные администраторы с изображениями]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Фаза 5: Парсеры и интеграция
|
||||||
|
|
||||||
|
### Задача 5.1: Обновление парсеров
|
||||||
|
- [ ] Найти все парсеры, которые создают TbImage
|
||||||
|
- [ ] Обновить их для создания filer.Image вместо TbImage
|
||||||
|
- [ ] Парсеры должны заполнять ImageMetadata с правильными значениями:
|
||||||
|
- `l_img_source = 'parser'`
|
||||||
|
- `l_img_reality` — зависит от контекста:
|
||||||
|
- Для **TbItem**: `l_img_reality = 'abstract'` (обложка из внешних источников, Discogs, etc)
|
||||||
|
- Для **TbOffer**: `l_img_reality = 'abstract'` (парсер берет из каталога)
|
||||||
|
- `f_img_confidence_score = [значение из парсера]`
|
||||||
|
- `i_img_sort = 0` (дефолт)
|
||||||
|
|
||||||
|
### Задача 5.1.1: Логика l_img_reality в разных контекстах
|
||||||
|
- [ ] **TbItem → Image (обложка альбома):** всегда `'abstract'`
|
||||||
|
- Signal: при создании ImageMetadata для TbItem → принудительно устанавливать `l_img_reality = 'abstract'`
|
||||||
|
- При редактировании в админке: поле `l_img_reality` должно быть read-only для TbItem
|
||||||
|
- [ ] **TbOffer → Image (фото товара в админке):**
|
||||||
|
- Дефолт при создании: `l_img_reality = 'real'` (человек загружает реальные фото)
|
||||||
|
- Signal: при создании ImageMetadata через админку → `l_img_reality = 'real'`
|
||||||
|
- [ ] **TbOffer → Image (парсер):**
|
||||||
|
- Дефолт при создании через парсер: `l_img_reality = 'abstract'`
|
||||||
|
- Signal: при создании ImageMetadata через парсер → `l_img_reality = 'abstract'`
|
||||||
|
- **Нужна логика определения источника:** как система узнает создана ли ImageMetadata парсером или админкой?
|
||||||
|
|
||||||
|
### Задача 5.2: Обновление других интеграций
|
||||||
|
- [ ] API endpoints (если есть)
|
||||||
|
- [ ] Скрипты обслуживания
|
||||||
|
- [ ] Любая логика, которая работает с изображениями
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Фаза 6: Тестирование и валидация
|
||||||
|
|
||||||
|
### Задача 6.1: Unit тесты
|
||||||
|
- [ ] Тест создания filer.Image → автоматическое создание ImageMetadata
|
||||||
|
- [ ] Тест обновления ImageMetadata → синхронизация дат в filer.Image
|
||||||
|
- [ ] Тест удаления ImageMetadata → filer.Image остаётся
|
||||||
|
- [ ] Тест удаления filer.Image → каскадное удаление ImageMetadata
|
||||||
|
|
||||||
|
### Задача 6.2: Интеграционные тесты
|
||||||
|
- [ ] Admin interface — создание/редактирование с инлайном
|
||||||
|
- [ ] Парсеры — создание корректных записей
|
||||||
|
- [ ] Шаблоны — корректный доступ к метаданным через `image.metadata.i_img_sort`
|
||||||
|
|
||||||
|
### Задача 6.3: Миграция данных (если база содержит данные)
|
||||||
|
- [ ] Запустить миграции
|
||||||
|
- [ ] Проверить целостность данных
|
||||||
|
- [ ] Проверить что все ImageMetadata связаны с filer.Image
|
||||||
|
- [ ] Проверить обратные связи работают
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Фаза 7: Финализация
|
||||||
|
|
||||||
|
### Задача 7.1: Удаление старого кода
|
||||||
|
- [ ] Удалить все ссылки на старый TbImage (если полностью переименовали на ImageMetadata)
|
||||||
|
- [ ] Очистить импорты
|
||||||
|
- [ ] Обновить комментарии и документацию
|
||||||
|
|
||||||
|
### Задача 7.2: Документирование
|
||||||
|
- [ ] Обновить ER-диаграмму в моделях
|
||||||
|
- [ ] Описать новую архитектуру в README или docs
|
||||||
|
- [ ] Примеры использования для разработчиков
|
||||||
|
|
||||||
|
### Задача 7.3: Развёртывание
|
||||||
|
- [ ] Code review
|
||||||
|
- [ ] Тестирование на staging
|
||||||
|
- [ ] Развёртывание на production с миграциями
|
||||||
|
- [ ] Мониторинг логов на ошибки
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Потенциальные ловушки
|
||||||
|
|
||||||
|
⚠️ **Определение источника создания ImageMetadata (админка vs парсер)**
|
||||||
|
Решение: добавить context или flag в сигнал, или использовать user_id в сигнале (если user_id = None → парсер, иначе админка)
|
||||||
|
|
||||||
|
⚠️ **TbItem.l_img_reality всегда должна быть 'abstract'**
|
||||||
|
Решение: signal принудительно устанавливает и делает read-only в админке
|
||||||
|
|
||||||
|
⚠️ **TbOffer может иметь как свои картинки, так и наследовать из TbItem**
|
||||||
|
Решение: M2M на filer.Image, админ может выбирать любые картинки и устанавливать l_img_reality на месте
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Оценка сложности
|
||||||
|
|
||||||
|
| Фаза | Сложность | Время (ч) | Приоритет |
|
||||||
|
|------|-----------|-----------|-----------|
|
||||||
|
| 1 | Низкая | 0.5 | Высокий |
|
||||||
|
| 2 | Средняя | 2-3 | Высокий |
|
||||||
|
| 3 | Средняя | 1-2 | Высокий |
|
||||||
|
| 4 | Низкая | 1 | Высокий |
|
||||||
|
| 5 | Средняя | 1-2 | Высокий |
|
||||||
|
| 6 | Средняя | 1-2 | Средний |
|
||||||
|
| 7 | Низкая | 1 | Средний |
|
||||||
|
|
||||||
|
**Итого:** ~8-13 часов
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Уточнения из ответов
|
||||||
|
|
||||||
|
✅ **1. Модель называется:** `TbImageMetadata` (переименуем TbImage)
|
||||||
|
✅ **2. Про performa & filer:**
|
||||||
|
- filer имеет две таблицы: File + Image (Image extends File через OneToOne)
|
||||||
|
- modified_at находится в таблице File (базовая для всех файлов)
|
||||||
|
- TbImageMetadata расширяет Image через OneToOne = это ОК, перформанс не критичен
|
||||||
|
- **Риск цикличности НИЗКИЙ:** обновляем ТОЛЬКО дату, не весь объект
|
||||||
|
|
||||||
|
✅ **3. M2M на TbOffer:**
|
||||||
|
- `k_offer_to_image` это M2M (Django автоматически создает промежуточную таблицу)
|
||||||
|
- ОК, оставляем M2M
|
||||||
|
|
||||||
|
✅ **4. Парсеры:**
|
||||||
|
- Создаются ПОЗЖЕ (в отдельном спринте)
|
||||||
|
- Сейчас делаем инфраструктуру для них
|
||||||
|
- Парсеры будут работать с filer напрямую, заполнять ImageMetadata вручную (или через свои сигналы позже)
|
||||||
|
|
||||||
|
✅ **5. Цикличность:**
|
||||||
|
- Филер обновляет modified_at при изменении файла — ОК, автоматическое
|
||||||
|
- Мы обновляем modified_at при изменении метаданных — **ТОЛЬКО дату**, не вызовет cascade
|
||||||
|
- **Цикличности НЕ будет** если правильно реализовать сигнал (через `update()`, не `save()`)
|
||||||
|
|
||||||
|
✅ **6. Определение источника сигнала (админка vs парсер):**
|
||||||
|
- **Решение:** Передавать через `using` параметр или context manager в signal'е
|
||||||
|
- **Для парсеров:** Парсеры могут создавать ImageMetadata вручную (без сигналов) или отключать сигналы
|
||||||
|
- **Для админки:** Сигналы работают стандартно
|
||||||
|
|
||||||
|
✅ **7. Миграцию делаешь сам** — ясно!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ФИНАЛЬНЫЙ ПЛАН: ПОРЯДОК РЕАЛИЗАЦИИ
|
||||||
|
|
||||||
|
### ЭТАП 1: ПОДГОТОВКА И МОДЕЛИ (БЕЗ МИГРАЦИЙ)
|
||||||
|
|
||||||
|
**Шаг 1.1:** Обновить модель TbImage → TbImageMetadata
|
||||||
|
- Переименовать класс с `TbImage` на `TbImageMetadata`
|
||||||
|
- Добавить `related_name='metadata'` на FilerImageField ← **ВСТРЕЧНЫЙ ИНДЕКС! Упростит миграции**
|
||||||
|
- **ПОКА ОСТАВИТЬ** t_img_created, t_img_updated (удалим после миграции данных)
|
||||||
|
- **НЕ создавать** миграцию на этом этапе
|
||||||
|
|
||||||
|
**Шаг 1.2:** Обновить все FK на картинки в моделях
|
||||||
|
- TbArticle: `k_article_to_image` ForeignKey(TbImage) → ForeignKey(filer.Image)
|
||||||
|
- **TbItem: добавить новое поле** `k_item_to_image = ForeignKey(filer.Image, blank=True, null=True)`
|
||||||
|
- TbOffer: `k_offer_to_image` M2M(TbImage) → M2M(filer.Image)
|
||||||
|
- Все остальные модели с картинками
|
||||||
|
- **НЕ создавать** миграции на этом этапе! Только обновляем код
|
||||||
|
|
||||||
|
**Шаг 1.3:** Обновить админку (без инлайнов пока)
|
||||||
|
- Обновить все FieldChoices в админке на новые FK
|
||||||
|
- **НЕ добавлять** ImageMetadataInline пока!
|
||||||
|
- **НЕ добавлять** миксин пока!
|
||||||
|
- Просто чтобы админка не ломалась с новыми FK
|
||||||
|
|
||||||
|
**РЕЗУЛЬТАТ:** Код скомпилирован, но БД не трогали. Всё готово к миграциям.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ЭТАП 2: МИГРАЦИИ (ТЫ ДЕЛАЕШЬ САМИ)
|
||||||
|
|
||||||
|
**Шаг 2.1:** Создать миграции Django
|
||||||
|
- Миграция на переименование класса TbImage → TbImageMetadata (если нужно переименовать таблицу, то custom миграция)
|
||||||
|
- Миграция на удаление старых FK из TbArticle, TbOffer и т.д.
|
||||||
|
- Миграция на добавление новых FK/M2M на filer.Image
|
||||||
|
- Миграция на добавление новых полей (TbItem.k_item_to_image и т.д.)
|
||||||
|
|
||||||
|
**Шаг 2.2:** Миграция данных
|
||||||
|
- Перенести существующие картинки из TbImage в filer.Image (если нужно)
|
||||||
|
- Привязать существующие записи к новым FK
|
||||||
|
- **Или:** Просто очистить TbImage и начать с нуля (если картинок мало)
|
||||||
|
|
||||||
|
**Шаг 2.3:** Удалить t_img_created и t_img_updated ПОСЛЕ успешной миграции
|
||||||
|
- Отдельная миграция на удаление этих двух полей
|
||||||
|
- Применяется ПОСЛЕ того как все данные перенесены и связи обновлены
|
||||||
|
- **ПОРЯДОК ВАЖЕН:** сначала новые FK/M2M, потом удаляем старые поля
|
||||||
|
|
||||||
|
**РЕЗУЛЬТАТ:** БД в новом состоянии, приложение рабочее (но без автоматизации через сигналы).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ЭТАП 3: СИГНАЛЫ И АДМИНКА (АВТОМАТИЗАЦИЯ)
|
||||||
|
|
||||||
|
**Шаг 3.1:** Создать Signal для auto-create TbImageMetadata
|
||||||
|
```
|
||||||
|
Когда: post_save filer.Image, created=True
|
||||||
|
Делает: автоматически создавать TbImageMetadata с дефолтами
|
||||||
|
Дефолты: i_img_sort=0, l_img_source='manual', l_img_reality зависит от контекста
|
||||||
|
```
|
||||||
|
|
||||||
|
**Шаг 3.2:** Создать Signal для синхронизации дат
|
||||||
|
```
|
||||||
|
Когда: post_save TbImageMetadata
|
||||||
|
Делает: обновляет filer.File.modified_at через SQL update (БЕЗ save()!)
|
||||||
|
Важно: modified_at из File, а не Image!
|
||||||
|
```
|
||||||
|
|
||||||
|
**Шаг 3.3:** Создать миксин для админки
|
||||||
|
```
|
||||||
|
ImageMetadataAdminMixin: автоматически добавляет инлайн для TbImageMetadata
|
||||||
|
Применяется к: TbArticleAdmin, TbOfferAdmin, и всем остальным
|
||||||
|
```
|
||||||
|
|
||||||
|
**Шаг 3.4:** Обновить админку с инлайнами
|
||||||
|
- TbArticleAdmin добавить миксин
|
||||||
|
- TbItemAdmin добавить миксин + читать-только l_img_reality для обложек
|
||||||
|
- TbOfferAdmin добавить миксин + M2M выбор картинок + l_img_reality редактируемо
|
||||||
|
- Остальные модели с картинками
|
||||||
|
|
||||||
|
**РЕЗУЛЬТАТ:** Всё работает автоматически. Юзер создает/редактирует картинки — метаданные появляются и синхронизируются.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ЭТАП 4: ПОДГОТОВКА К ПАРСЕРАМ (FUTURE-PROOF)
|
||||||
|
|
||||||
|
**Шаг 4.1:** Документировать как парсерам использовать новую архитектуру
|
||||||
|
- Парсеры создают filer.Image напрямую
|
||||||
|
- TbImageMetadata автоматически создается через сигнал
|
||||||
|
- Парсеры могут переопределять значения через direct update
|
||||||
|
|
||||||
|
**Шаг 4.2:** Создать helper для парсеров (optional)
|
||||||
|
```
|
||||||
|
Функция: create_image_from_url(url, l_img_source, l_img_reality, ...)
|
||||||
|
Делает: загружает в filer.Image + заполняет TbImageMetadata правильно
|
||||||
|
Это обычно нужно потом, но архитектура к этому готова
|
||||||
|
```
|
||||||
|
|
||||||
|
**Шаг 4.3:** Отключение сигналов для парсеров (если нужно)
|
||||||
|
```
|
||||||
|
Если парсер хочет создавать без сигналов:
|
||||||
|
- Парсер может установить какой-то флаг (например, signals.disconnect())
|
||||||
|
- Или парсер создает все сам без сигналов
|
||||||
|
- ИЛИ передавать context через signal'ы (позже если понадобится)
|
||||||
|
```
|
||||||
|
|
||||||
|
**РЕЗУЛЬТАТ:** Архитектура готова для будущих парсеров. Они смогут работать либо через сигналы, либо вручную.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## УТОЧНЕНИЕ: КАРТИНКИ ТОЛЬКО В TBIMAGE
|
||||||
|
|
||||||
|
**Важный факт:** Все картинки сейчас в filer привязаны ТОЛЬКО к TbImage, больше никуда не привязаны.
|
||||||
|
|
||||||
|
**Это упрощает миграции!**
|
||||||
|
- Нет данных в других моделях которые нужно конвертировать
|
||||||
|
- `related_name='metadata'` на FilerImageField даст встречный доступ: `filer_image.metadata`
|
||||||
|
- Миграция будет проще: просто переименовать таблицу, добавить новые FK структуры, готово
|
||||||
|
|
||||||
|
**Шаги миграции будут:**
|
||||||
|
1. Переименовать TbImage → TbImageMetadata (в таблице)
|
||||||
|
2. Добавить новые поля для TbItem.k_item_to_image
|
||||||
|
3. Очистить/переинициализировать существующие данные если нужно
|
||||||
|
4. ПОСЛЕ всего этого — удалить t_img_created, t_img_updated
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ОТВЕТ НА ВОПРОС: КОГДА УДАЛЯТЬ t_img_created И t_img_updated?
|
||||||
|
|
||||||
|
**Правильный порядок:**
|
||||||
|
|
||||||
|
1. **Этап 1 (код):** Оставить поля в коде модели (пока!)
|
||||||
|
2. **Этап 2 (миграции) — ПЕРВЫЕ миграции:**
|
||||||
|
- Переименование TbImage → TbImageMetadata
|
||||||
|
- Добавление новых FK структур
|
||||||
|
- Миграция данных
|
||||||
|
3. **Этап 2 (миграции) — ПОСЛЕДНЯЯ миграция:**
|
||||||
|
- Удалить t_img_created и t_img_updated из БД
|
||||||
|
4. **ПОТОМ, Этап 1 (код):** Удалить эти поля из кода модели
|
||||||
|
|
||||||
|
**ПОЧЕМУ такой порядок?**
|
||||||
|
- Сначала применяем миграции НОВЫХ структур — убеждаемся что всё работает
|
||||||
|
- ПОТОМ удаляем старые поля — когда новая структура стабильна
|
||||||
|
- Если что-то пошло не так — есть откат через старые поля
|
||||||
|
|
||||||
|
**ПРАКТИЧЕСКИ:**
|
||||||
|
- Этап 1: TbImageMetadata всё ещё имеет `t_img_created`, `t_img_updated` в коде
|
||||||
|
- Ты создаёшь первую миграцию (переименование, новые FK)
|
||||||
|
- Ты создаёшь вторую миграцию (удаление дат из БД)
|
||||||
|
- Применяешь обе
|
||||||
|
- ПОТОМ я удаляю эти поля из самого кода модели (простое удаление из файла)
|
||||||
|
|
||||||
|
1. **ЭТАП 1 (2-3 часа)** — Обновить код моделей и админки, БД не трогаем
|
||||||
|
2. **ЭТАП 2 (30 мин - 2 часа)** — Написать миграции и применить их (ты делаешь!)
|
||||||
|
3. **ЭТАП 3 (2-3 часа)** — Написать сигналы, админка миксины, протестировать
|
||||||
|
4. **ЭТАП 4 (30 мин)** — Документировать для будущих парсеров
|
||||||
|
|
||||||
|
**ОБЩЕЕ ВРЕМЯ:** ~7-9 часов
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## КЛЮЧЕВЫЕ МОМЕНТЫ
|
||||||
|
|
||||||
|
🔑 **Этап 1 БЕЗ МИГРАЦИЙ** — сначала весь код готовый, потом БД меняем
|
||||||
|
🔑 **Этап 2 ТЫ ДЕЛАЕШЬ** — я не трогаю, ты контролируешь
|
||||||
|
🔑 **Сигналы ТОЛЬКО ПОСЛЕ миграций** — иначе конфликты
|
||||||
|
🔑 **Цикличность ИСКЛЮЧЕНА** — обновляем только дату через SQL update()
|
||||||
|
🔑 **Парсеры готовы** — архитектура к ним готова, но коды парсеров делаешь позже
|
||||||
|
🔑 **Админка будет удобная** — один inline с метаданными внутри FK выбора
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Готов начинать с ЭТАПА 1?**
|
||||||
Reference in New Issue
Block a user