[WEB-5398] fix: enhance bulk update handling for soft deletions and improve queryset management (#4722)

This commit is contained in:
Dheeraj Kumar Ketireddy
2025-11-11 19:24:12 +05:30
committed by GitHub
parent 4c5055a9da
commit f8596c8feb
3 changed files with 23 additions and 19 deletions
+15 -1
View File
@@ -47,9 +47,23 @@ class BulkOperationHooks:
Custom update method to trigger a signal with
all the updated objs on bulk update.
"""
# Check if this is a soft deletion (deleted_at is being set)
is_soft_delete = "deleted_at" in kwargs and kwargs["deleted_at"] is not None
if is_soft_delete:
# Capture PKs before update to handle soft deletion case
# where self queryset will become empty after deleted_at is set
deleted_pks = list(self.values_list("pk", flat=True))
rows = super().update(**kwargs)
if rows:
post_bulk_update.send(sender=self.__class__, model=self.model, objs=self)
if is_soft_delete:
# Use all_objects to get unfiltered queryset including soft-deleted records
objs = self.model.all_objects.filter(pk__in=deleted_pks)
else:
# For regular updates, use the current queryset
objs = self
post_bulk_update.send(sender=self.__class__, model=self.model, objs=objs)
return rows
@transaction.atomic
+5 -15
View File
@@ -44,9 +44,7 @@ def update_index_on_bulk_create_update(sender, **kwargs):
# Queue all objects as a single bulk update - batch processor will drain all queued items
obj_ids = [str(obj.id) for obj in objs_list]
semantic_fields_changed = check_bulk_semantic_fields_changed(
model_name, objs_list
)
semantic_fields_changed = check_bulk_semantic_fields_changed(model_name, objs_list)
queue_bulk_updates_for_batch(
model_name=model_name,
obj_ids=obj_ids,
@@ -84,9 +82,7 @@ class BatchedCelerySignalProcessor(CelerySignalProcessor):
from django_opensearch_dsl.registries import registry
if instance.__class__ in registry._models:
semantic_fields_changed = self._check_semantic_fields_changed(
instance, **kwargs
)
semantic_fields_changed = self._check_semantic_fields_changed(instance, **kwargs)
# Queue update for batch processing - cascade to related documents handled during batch processing
transaction.on_commit(
@@ -122,9 +118,7 @@ class BatchedCelerySignalProcessor(CelerySignalProcessor):
updated_fields = set(update_fields) if update_fields else set()
semantic_fields_set = set(semantic_fields)
if semantic_fields_set.intersection(updated_fields):
logger.info(
f"Semantic fields in update_fields for {model_name} {instance.pk}, semantic fields changed"
)
logger.info(f"Semantic fields in update_fields for {model_name} {instance.pk}, semantic fields changed")
return True
else:
# Check if semantic fields have actually changed by comparing with original values
@@ -134,9 +128,7 @@ class BatchedCelerySignalProcessor(CelerySignalProcessor):
original_value = getattr(instance, original_attr)
current_value = getattr(instance, field, None)
if original_value != current_value:
logger.info(
f"Semantic field '{field}' changed for {model_name} {instance.pk}"
)
logger.info(f"Semantic field '{field}' changed for {model_name} {instance.pk}")
return True
return False
@@ -149,9 +141,7 @@ class BatchedCelerySignalProcessor(CelerySignalProcessor):
# Filter: Only process search-relevant models
if not is_model_search_relevant(model_name):
logger.debug(
f"Skipping non-search-relevant model for deletion: {model_name}"
)
logger.debug(f"Skipping non-search-relevant model for deletion: {model_name}")
return
if self.instance_requires_update(instance):
@@ -128,11 +128,11 @@ class IssueDocument(BaseDocument):
def get_instances_from_related(self, related_instance):
if isinstance(related_instance, Project):
qs = related_instance.project_issue(manager="objects").all()
qs = related_instance.project_issue(manager="all_objects").all()
elif isinstance(related_instance, ProjectMember):
qs = related_instance.project.project_issue(manager="objects").all()
qs = related_instance.project.project_issue(manager="all_objects").all()
else:
qs = self.django.model.objects.none()
qs = self.django.model.all_objects.none()
return self.apply_related_to_queryset(qs)
def prepare_project_is_archived(self, instance):