minor: план переделки работы с картинками! Через сигналы! Уточнено.
This commit is contained in:
@@ -35,8 +35,11 @@
|
|||||||
|
|
||||||
## Фаза 2: Модификация моделей
|
## Фаза 2: Модификация моделей
|
||||||
|
|
||||||
### Задача 2.1: Переименование и переструктурирование TbImage
|
### Задача 2.1: Переименование и упрощение TbImageMetadata
|
||||||
- [ ] Переименовать класс TbImage → ImageMetadata
|
|
||||||
|
Архитектура: **МИНИМАЛИЗМ + JSON для гибкости**
|
||||||
|
|
||||||
|
- [ ] Переименовать класс TbImage → TbImageMetadata
|
||||||
- [ ] Добавить/проверить `related_name='metadata'` на FilerImageField
|
- [ ] Добавить/проверить `related_name='metadata'` на FilerImageField
|
||||||
```python
|
```python
|
||||||
image = FilerImageField(
|
image = FilerImageField(
|
||||||
@@ -44,11 +47,27 @@
|
|||||||
related_name='metadata' # ← для доступа: filer_image.metadata
|
related_name='metadata' # ← для доступа: filer_image.metadata
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
- [ ] **УДАЛИТЬ** поля из ImageMetadata:
|
- [ ] **ОСТАВИТЬ в TbImageMetadata:**
|
||||||
- `t_img_created` (используем filer.Image.uploaded_at или эквивалент)
|
- `image` (OneToOneField → filer.Image)
|
||||||
- `t_img_updated` (синхронизируем с filer.Image.modified_at через сигнал)
|
- `i_img_sort` (IntegerField, db_index=True) ← ТОЛЬКО это индексируется!
|
||||||
|
- `j_img_metadata` (JSONField, null=True, blank=True) ← всё остальное сюда
|
||||||
|
|
||||||
|
- [ ] **УДАЛИТЬ ВСЕ остальные поля:**
|
||||||
|
- ❌ `l_img_source` (будет в JSON если нужно)
|
||||||
|
- ❌ `l_img_reality` (дефолт по контексту: TbItem→abstract, TbOffer админка→real, иное→abstract)
|
||||||
|
- ❌ `s_img_src_url` (в JSON если нужно)
|
||||||
|
- ❌ `f_img_confidence_score` (в JSON если нужно, парсеры его заполнят)
|
||||||
|
- ❌ `t_img_created` (используем filer.File.uploaded_at)
|
||||||
|
- ❌ `t_img_updated` (используем filer.File.modified_at)
|
||||||
|
|
||||||
- [ ] Обновить `Meta.verbose_name` на "Метаданные изображения"
|
- [ ] Обновить `Meta.verbose_name` на "Метаданные изображения"
|
||||||
- [ ] Обновить `Meta.ordering` (убрать временные поля если они там были)
|
- [ ] Обновить `Meta.ordering` — просто `('i_img_sort',)`
|
||||||
|
|
||||||
|
**ПОЧЕМУ?**
|
||||||
|
- Парсеров еще нет, не знаем какие поля реально нужны
|
||||||
|
- Справочные поля (источник, тип, достоверность) не нужны для индексации/фильтрации
|
||||||
|
- JSON дает гибкость: позже когда парсеры появятся, просто добавим новые ключи или выносим в таблицу если нужен индекс
|
||||||
|
- YAGNI: не создаем сложность пока она не нужна
|
||||||
|
|
||||||
### Задача 2.2: Обновление FK на изображения во всех моделях
|
### Задача 2.2: Обновление FK на изображения во всех моделях
|
||||||
- [ ] TbArticle: `k_article_to_image` — ForeignKey(TbImage) → ForeignKey(filer.Image)
|
- [ ] TbArticle: `k_article_to_image` — ForeignKey(TbImage) → ForeignKey(filer.Image)
|
||||||
@@ -77,35 +96,34 @@
|
|||||||
|
|
||||||
## Фаза 3: Сигналы и автоматизация
|
## Фаза 3: Сигналы и автоматизация
|
||||||
|
|
||||||
### Задача 3.1: Signal на создание filer.Image → auto-create ImageMetadata
|
### Задача 3.1: Signal на создание filer.Image → auto-create TbImageMetadata
|
||||||
```python
|
```python
|
||||||
# В apps.py
|
# В apps.py
|
||||||
@receiver(post_save, sender=filer.models.Image)
|
@receiver(post_save, sender=filer.models.Image)
|
||||||
def auto_create_image_metadata(sender, instance, created, **kwargs):
|
def auto_create_image_metadata(sender, instance, created, **kwargs):
|
||||||
"""При создании filer.Image автоматически создавать ImageMetadata с дефолтами"""
|
"""При создании filer.Image автоматически создавать TbImageMetadata"""
|
||||||
if created:
|
if created:
|
||||||
ImageMetadata.objects.get_or_create(
|
TbImageMetadata.objects.get_or_create(
|
||||||
image=instance,
|
image=instance,
|
||||||
defaults={
|
defaults={
|
||||||
'i_img_sort': 0,
|
'i_img_sort': 0,
|
||||||
'l_img_source': 'manual', # или 'parser' если известно
|
# j_img_metadata оставляем пустым, заполняется по мере необходимости
|
||||||
'l_img_reality': 'abstract',
|
|
||||||
'f_img_confidence_score': 10.0,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Задача 3.2: Обратные сигналы — синхронизация дат
|
### Задача 3.2: Обратный сигнал — синхронизация дат (опционально)
|
||||||
```python
|
```python
|
||||||
# В apps.py
|
# В apps.py — ЕСЛИ нужно обновлять modified_at в filer при изменении i_img_sort
|
||||||
@receiver(post_save, sender=ImageMetadata)
|
@receiver(post_save, sender=TbImageMetadata)
|
||||||
def sync_image_modification_date(sender, instance, **kwargs):
|
def sync_image_modification_date(sender, instance, **kwargs):
|
||||||
"""При обновлении ImageMetadata обновлять modified_at в filer.Image"""
|
"""При обновлении i_img_sort обновляем modified_at в filer.File"""
|
||||||
# Обновляем filer.Image.modified_at = now()
|
# Обновляем filer.File.modified_at через SQL update (без повторного save()!)
|
||||||
# Нужно понять как именно это сделать в filer (direct update без повторного save())
|
from filer.models import File
|
||||||
|
File.objects.filter(id=instance.image.id).update(modified_at=now())
|
||||||
```
|
```
|
||||||
|
|
||||||
**Важно:** Нужно избежать циклических сохранений (Image → Metadata → Image → ...)
|
**ПРИМЕЧАНИЕ:** Сигнал опционален. Если не нужно обновлять дату каждый раз при изменении сортировки — можно пропустить.
|
||||||
|
|
||||||
### Задача 3.3: Сигнал на удаление
|
### Задача 3.3: Сигнал на удаление
|
||||||
- [ ] При удалении ImageMetadata → не удаляем filer.Image (метаданные удаляются, файл остаётся)
|
- [ ] При удалении ImageMetadata → не удаляем filer.Image (метаданные удаляются, файл остаётся)
|
||||||
@@ -122,52 +140,58 @@ class ImageMetadataAdminMixin:
|
|||||||
# Автоматически добавлять инлайн ImageMetadata при наличии FK на Image
|
# Автоматически добавлять инлайн ImageMetadata при наличии FK на Image
|
||||||
```
|
```
|
||||||
|
|
||||||
### Задача 4.2: Создание ImageMetadataInline
|
### Задача 4.2: Создание TbImageMetadataInline
|
||||||
- [ ] StackedInline или TabularInline?
|
- [ ] StackedInline для TbImageMetadata
|
||||||
- [ ] Какие поля показывать: `i_img_sort`, `l_img_source`, `l_img_reality`, `f_img_confidence_score`
|
- [ ] Поля: `i_img_sort`, `j_img_metadata`
|
||||||
- [ ] Read-only поля? Или всё редактируемо?
|
- [ ] `j_img_metadata` как JSON editor (админка Django поддерживает JSONField)
|
||||||
|
- [ ] Всё просто и минималистично
|
||||||
|
|
||||||
### Задача 4.3: Обновление админок моделей
|
### Задача 4.3: Обновление админок моделей
|
||||||
- [ ] TbArticleAdmin — добавить ImageMetadataInline
|
- [ ] TbArticleAdmin — добавить TbImageMetadataAdminMixin
|
||||||
- [ ] **TbItemAdmin — добавить ImageMetadataInline для обложки (cover image)**
|
- [ ] **TbItemAdmin — добавить мixin для обложки**
|
||||||
- Поле `k_item_to_image` — ForeignKey с инлайном ImageMetadata
|
- Поле `k_item_to_image` с инлайном TbImageMetadata
|
||||||
- ImageMetadata: `l_img_reality` должно быть read-only = `'abstract'`
|
- `i_img_sort` редактируемо
|
||||||
- [ ] **TbOfferAdmin — добавить связь к изображениям с множественным выбором**
|
- `j_img_metadata` может содержать замечания ("абстрактная", "с Discogs" и т.д.) если нужно
|
||||||
- M2M к filer.Image с инлайном ImageMetadata
|
- [ ] **TbOfferAdmin — добавить мixin для картинок товара**
|
||||||
- ImageMetadata: `l_img_reality` редактируемо (админ может менять на `'real'`)
|
- M2M к filer.Image с инлайном TbImageMetadata
|
||||||
|
- `i_img_sort` редактируемо (для сортировки фото товара)
|
||||||
|
- `j_img_metadata` редактируемо (админ может добавить замечания)
|
||||||
- [ ] [Все остальные администраторы с изображениями]
|
- [ ] [Все остальные администраторы с изображениями]
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Фаза 5: Парсеры и интеграция
|
## Фаза 5: Парсеры и интеграция
|
||||||
|
|
||||||
### Задача 5.1: Обновление парсеров
|
### Задача 5.1: Подготовка к парсерам (FUTURE-PROOF)
|
||||||
- [ ] Найти все парсеры, которые создают TbImage
|
- [ ] Найти все парсеры (их еще нет, но логика готова)
|
||||||
- [ ] Обновить их для создания filer.Image вместо TbImage
|
- [ ] Парсеры смогут заполнять `j_img_metadata` с любыми данными:
|
||||||
- [ ] Парсеры должны заполнять ImageMetadata с правильными значениями:
|
```json
|
||||||
- `l_img_source = 'parser'`
|
{
|
||||||
- `l_img_reality` — зависит от контекста:
|
"source": "discogs",
|
||||||
- Для **TbItem**: `l_img_reality = 'abstract'` (обложка из внешних источников, Discogs, etc)
|
"reality": "abstract",
|
||||||
- Для **TbOffer**: `l_img_reality = 'abstract'` (парсер берет из каталога)
|
"confidence": 0.95,
|
||||||
- `f_img_confidence_score = [значение из парсера]`
|
"original_url": "https://...",
|
||||||
- `i_img_sort = 0` (дефолт)
|
"notes": "..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- [ ] `i_img_sort` может устанавливаться парсером если нужна специальная сортировка
|
||||||
|
- [ ] **Дефолтная логика (без парсера):**
|
||||||
|
- TbItem → картинка? → j_img_metadata = `{"reality": "abstract"}`
|
||||||
|
- TbOffer + админка → картинка? → j_img_metadata пустой или `{}`
|
||||||
|
- На фронте: если не указано → apply defaults
|
||||||
|
|
||||||
### Задача 5.1.1: Логика l_img_reality в разных контекстах
|
### Задача 5.2: Миграция существующих данных (ТЫ ДЕЛАЕШЬ)
|
||||||
- [ ] **TbItem → Image (обложка альбома):** всегда `'abstract'`
|
- [ ] Перенести все TbImage в TbImageMetadata (переименование таблицы)
|
||||||
- Signal: при создании ImageMetadata для TbItem → принудительно устанавливать `l_img_reality = 'abstract'`
|
- [ ] Заполнить `i_img_sort` из старого поля (если было)
|
||||||
- При редактировании в админке: поле `l_img_reality` должно быть read-only для TbItem
|
- [ ] `j_img_metadata` заполнить на основе старых полей если они важны
|
||||||
- [ ] **TbOffer → Image (фото товара в админке):**
|
```json
|
||||||
- Дефолт при создании: `l_img_reality = 'real'` (человек загружает реальные фото)
|
{
|
||||||
- Signal: при создании ImageMetadata через админку → `l_img_reality = 'real'`
|
"old_l_img_source": "...",
|
||||||
- [ ] **TbOffer → Image (парсер):**
|
"old_l_img_reality": "...",
|
||||||
- Дефолт при создании через парсер: `l_img_reality = 'abstract'`
|
"old_confidence": "..."
|
||||||
- Signal: при создании ImageMetadata через парсер → `l_img_reality = 'abstract'`
|
}
|
||||||
- **Нужна логика определения источника:** как система узнает создана ли ImageMetadata парсером или админкой?
|
```
|
||||||
|
- [ ] Или просто пусто, парсеры потом заполнят правильно
|
||||||
### Задача 5.2: Обновление других интеграций
|
|
||||||
- [ ] API endpoints (если есть)
|
|
||||||
- [ ] Скрипты обслуживания
|
|
||||||
- [ ] Любая логика, которая работает с изображениями
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -431,15 +455,42 @@ ImageMetadataAdminMixin: автоматически добавляет инла
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## КЛЮЧЕВЫЕ МОМЕНТЫ
|
## НОВАЯ АРХИТЕКТУРА: МИНИМАЛИЗМ + JSON
|
||||||
|
|
||||||
🔑 **Этап 1 БЕЗ МИГРАЦИЙ** — сначала весь код готовый, потом БД меняем
|
**TbImageMetadata содержит:**
|
||||||
🔑 **Этап 2 ТЫ ДЕЛАЕШЬ** — я не трогаю, ты контролируешь
|
- `image` (OneToOne → filer.Image)
|
||||||
🔑 **Сигналы ТОЛЬКО ПОСЛЕ миграций** — иначе конфликты
|
- `i_img_sort` (IntegerField, db_index=True) ← **ТОЛЬКО поле с индексом!**
|
||||||
🔑 **Цикличность ИСКЛЮЧЕНА** — обновляем только дату через SQL update()
|
- `j_img_metadata` (JSONField, null=True) ← **Всё остальное сюда**
|
||||||
🔑 **Парсеры готовы** — архитектура к ним готова, но коды парсеров делаешь позже
|
|
||||||
🔑 **Админка будет удобная** — один inline с метаданными внутри FK выбора
|
**Почему?**
|
||||||
|
- ✅ Парсеров еще нет → не знаем какие поля реально нужны
|
||||||
|
- ✅ Справочные данные не используются для фильтрации/индексации
|
||||||
|
- ✅ JSON гибко хранит всё: источник, тип, достоверность, URL и т.д.
|
||||||
|
- ✅ Если потом парсеры потребуют индексы → просто выносим поле из JSON
|
||||||
|
- ✅ Дефолтная логика: TbItem → abstract, TbOffer админка → любое значение
|
||||||
|
|
||||||
|
**Пример j_img_metadata:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"source": "parser",
|
||||||
|
"reality": "abstract",
|
||||||
|
"confidence": 0.95,
|
||||||
|
"original_url": "https://discogs.com/...",
|
||||||
|
"notes": "Обложка из каталога"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Готов начинать с ЭТАПА 1?**
|
## ИТОГОВЫЙ ПОРЯДОК РЕАЛИЗАЦИИ (ОБНОВЛЕНО)
|
||||||
|
|
||||||
|
🔑 **Этап 1 БЕЗ МИГРАЦИЙ** — сначала весь код готовый, потом БД меняем
|
||||||
|
🔑 **Этап 2 ТЫ ДЕЛАЕШЬ** — я не трогаю, ты контролируешь миграции
|
||||||
|
🔑 **Архитектура МИНИМАЛИСТИЧНА** — только i_img_sort индексируется, всё остальное в JSON
|
||||||
|
🔑 **Гибкость МАКСИМАЛЬНАЯ** — JSON позволяет добавлять любые данные без миграций БД
|
||||||
|
🔑 **Парсеры готовы** — при создании просто заполняют j_img_metadata как нужно
|
||||||
|
🔑 **Дефолтная логика** — на фронте если метаданных нет → применяем defaults по контексту
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Готов начинать с ЭТАПА 1?** Или еще вопросы/уточнения?
|
||||||
|
|||||||
Reference in New Issue
Block a user