131 lines
4.6 KiB
Python
131 lines
4.6 KiB
Python
"""
|
|
Cascade signals for Planning models
|
|
Handles status updates and relationship cleanup when parent records are deleted
|
|
"""
|
|
import logging
|
|
from django.db.models.signals import pre_delete, post_save
|
|
from django.dispatch import receiver
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@receiver(pre_delete, sender='planner.Clusters')
|
|
def handle_cluster_soft_delete(sender, instance, **kwargs):
|
|
"""
|
|
When a Cluster is deleted:
|
|
- Set Keywords.cluster = NULL
|
|
- Reset Keywords.status to 'new'
|
|
- Set ContentIdeas.keyword_cluster = NULL
|
|
- Reset ContentIdeas.status to 'new'
|
|
"""
|
|
from igny8_core.business.planning.models import Keywords, ContentIdeas
|
|
|
|
# Check if this is a soft delete (is_deleted=True) vs hard delete
|
|
# Soft deletes trigger delete() which calls soft_delete()
|
|
if hasattr(instance, 'is_deleted') and instance.is_deleted:
|
|
return # Skip if already soft-deleted
|
|
|
|
try:
|
|
# Update related Keywords - clear cluster FK and reset status
|
|
updated_keywords = Keywords.objects.filter(cluster=instance).update(
|
|
cluster=None,
|
|
status='new'
|
|
)
|
|
if updated_keywords:
|
|
logger.info(
|
|
f"[Cascade] Cluster '{instance.name}' (ID: {instance.id}) deleted: "
|
|
f"Reset {updated_keywords} keywords to status='new', cluster=NULL"
|
|
)
|
|
|
|
# Update related ContentIdeas - clear cluster FK and reset status
|
|
updated_ideas = ContentIdeas.objects.filter(keyword_cluster=instance).update(
|
|
keyword_cluster=None,
|
|
status='new'
|
|
)
|
|
if updated_ideas:
|
|
logger.info(
|
|
f"[Cascade] Cluster '{instance.name}' (ID: {instance.id}) deleted: "
|
|
f"Reset {updated_ideas} content ideas to status='new', keyword_cluster=NULL"
|
|
)
|
|
|
|
except Exception as e:
|
|
logger.error(f"[Cascade] Error handling cluster deletion cascade: {e}", exc_info=True)
|
|
|
|
|
|
@receiver(pre_delete, sender='planner.ContentIdeas')
|
|
def handle_idea_soft_delete(sender, instance, **kwargs):
|
|
"""
|
|
When a ContentIdea is deleted:
|
|
- Set Tasks.idea = NULL (don't delete tasks, they may have content)
|
|
- Log orphaned tasks
|
|
"""
|
|
from igny8_core.business.content.models import Tasks
|
|
|
|
if hasattr(instance, 'is_deleted') and instance.is_deleted:
|
|
return
|
|
|
|
try:
|
|
# Update related Tasks - clear idea FK
|
|
updated_tasks = Tasks.objects.filter(idea=instance).update(idea=None)
|
|
if updated_tasks:
|
|
logger.info(
|
|
f"[Cascade] ContentIdea '{instance.idea_title}' (ID: {instance.id}) deleted: "
|
|
f"Cleared idea reference from {updated_tasks} tasks"
|
|
)
|
|
|
|
except Exception as e:
|
|
logger.error(f"[Cascade] Error handling content idea deletion cascade: {e}", exc_info=True)
|
|
|
|
|
|
@receiver(pre_delete, sender='writer.Tasks')
|
|
def handle_task_soft_delete(sender, instance, **kwargs):
|
|
"""
|
|
When a Task is deleted:
|
|
- Set Content.task = NULL
|
|
"""
|
|
from igny8_core.business.content.models import Content
|
|
|
|
if hasattr(instance, 'is_deleted') and instance.is_deleted:
|
|
return
|
|
|
|
try:
|
|
# Update related Content - clear task FK
|
|
updated_content = Content.objects.filter(task=instance).update(task=None)
|
|
if updated_content:
|
|
logger.info(
|
|
f"[Cascade] Task '{instance.title}' (ID: {instance.id}) deleted: "
|
|
f"Cleared task reference from {updated_content} content items"
|
|
)
|
|
|
|
except Exception as e:
|
|
logger.error(f"[Cascade] Error handling task deletion cascade: {e}", exc_info=True)
|
|
|
|
|
|
@receiver(pre_delete, sender='writer.Content')
|
|
def handle_content_soft_delete(sender, instance, **kwargs):
|
|
"""
|
|
When Content is deleted:
|
|
- Soft delete related Images (cascade soft delete)
|
|
- Clear PublishingRecord references
|
|
"""
|
|
from igny8_core.business.content.models import Images
|
|
|
|
if hasattr(instance, 'is_deleted') and instance.is_deleted:
|
|
return
|
|
|
|
try:
|
|
# Soft delete related Images
|
|
related_images = Images.objects.filter(content=instance)
|
|
for image in related_images:
|
|
image.soft_delete(reason='cascade_from_content')
|
|
|
|
count = related_images.count()
|
|
if count:
|
|
logger.info(
|
|
f"[Cascade] Content '{instance.title}' (ID: {instance.id}) deleted: "
|
|
f"Soft deleted {count} related images"
|
|
)
|
|
|
|
except Exception as e:
|
|
logger.error(f"[Cascade] Error handling content deletion cascade: {e}", exc_info=True)
|