@@ -531,30 +531,43 @@ def _collapse_changes_for_object(changes, logger):
531531 """
532532 Collapse a list of ObjectChanges for a single object.
533533 Returns: (final_action, merged_data, last_change)
534+
535+ Simplified DELETE logic: If DELETE appears anywhere in the changes,
536+ ignore all other changes and keep only the DELETE.
534537 """
535538 if not changes :
536539 return None , None , None
537540
538541 # Sort by time (oldest first)
539542 changes = sorted (changes , key = lambda c : c .time )
540543
541- first_action = changes [0 ].action
542- last_action = changes [- 1 ].action
543- last_change = changes [- 1 ]
544+ # Check if there's a DELETE anywhere in the changes
545+ has_delete = any (c .action == 'delete' for c in changes )
546+ has_create = any (c .action == 'create' for c in changes )
547+
548+ logger .debug (f" Collapsing { len (changes )} changes..." )
549+
550+ if has_delete :
551+ if has_create :
552+ # CREATE + DELETE = skip entirely
553+ logger .debug (" -> Action: SKIP (created and deleted in branch)" )
554+ return 'skip' , None , changes [- 1 ]
555+ else :
556+ # Just DELETE (ignore all other changes like updates)
557+ # Use prechange_data from first ObjectChange (the original state)
558+ logger .debug (f" -> Action: DELETE (keeping only DELETE, ignoring { len (changes ) - 1 } other changes)" )
559+ delete_change = next (c for c in changes if c .action == 'delete' )
544560
545- logger .debug (f" Collapsing { len (changes )} changes: first={ first_action } , last={ last_action } " )
561+ # Copy prechange_data from first change to ensure we have the original state
562+ delete_change .prechange_data = changes [0 ].prechange_data
546563
547- # Case 1: Created then deleted -> skip entirely
548- if first_action == 'create' and last_action == 'delete' :
549- logger .debug (" -> Action: SKIP (created and deleted in branch)" )
550- return 'skip' , None , last_change
564+ return 'delete' , delete_change .prechange_data , delete_change
551565
552- # Case 2: Deleted -> just delete (should be only one delete)
553- if last_action == 'delete' :
554- logger .debug (" -> Action: DELETE" )
555- return 'delete' , changes [- 1 ].prechange_data , last_change
566+ # No DELETE - handle CREATE or UPDATEs
567+ first_action = changes [0 ].action
568+ last_change = changes [- 1 ]
556569
557- # Case 3: Created (with possible updates) -> single create
570+ # Created (with possible updates) -> single create
558571 if first_action == 'create' :
559572 merged_data = {}
560573 for change in changes :
@@ -564,7 +577,7 @@ def _collapse_changes_for_object(changes, logger):
564577 logger .debug (f" -> Action: CREATE (collapsed { len (changes )} changes)" )
565578 return 'create' , merged_data , last_change
566579
567- # Case 4: Only updates -> single update
580+ # Only updates -> single update
568581 merged_data = {}
569582 # Start with prechange_data of first change as baseline
570583 if changes [0 ].prechange_data :
@@ -776,28 +789,29 @@ def _order_collapsed_changes(collapsed_changes, logger):
776789 ordered_keys = Branch ._topological_sort_with_cycle_detection (to_process , logger )
777790
778791 # Group by model and refine order within each model
779- logger .info ("Refining order within models (updates before creates)..." )
792+ logger .info ("Refining order within models (deletes before creates to free unique constraints )..." )
780793 by_model = defaultdict (list )
781794 for key in ordered_keys :
782795 collapsed = to_process [key ]
783796 by_model [collapsed .model_class ].append (collapsed )
784797
785- # Within each model: updates, then creates, then deletes
798+ # Within each model: updates, then deletes, then creates
799+ # This ensures deletes free up unique constraints (like slugs) before creates claim them
786800 result = []
787801 for model_class , changes in by_model .items ():
788802 updates = [c for c in changes if c .final_action == 'update' ]
789- creates = [c for c in changes if c .final_action == 'create' ]
790803 deletes = [c for c in changes if c .final_action == 'delete' ]
804+ creates = [c for c in changes if c .final_action == 'create' ]
791805
792806 if updates or creates or deletes :
793807 logger .debug (
794808 f" { model_class .__name__ } : { len (updates )} updates, "
795- f"{ len (creates )} creates , { len (deletes )} deletes "
809+ f"{ len (deletes )} deletes , { len (creates )} creates "
796810 )
797811
798812 result .extend (updates )
799- result .extend (creates )
800813 result .extend (deletes )
814+ result .extend (creates )
801815
802816 logger .info (f"Ordering complete: { len (result )} changes to apply" )
803817 return result
0 commit comments