1010from django .db import transaction
1111from django .db .models import CharField
1212from django .db .models import F
13+ from django .db .models import Exists
14+ from django .db .models import OuterRef
1315from django .db .models import Q
16+ from django .db .models import Subquery
1417from django .db .models import signals
1518from django .db .models import Value
1619from django .db .models .fields import BooleanField
17- from django .db .models .functions import NullIf
20+ from django .db .models .functions import NullIf , Cast
1821from django .db .utils import IntegrityError
1922from django .db .utils import OperationalError
2023from django .utils import timezone
2528from morango .constants import transfer_statuses
2629from morango .constants .capabilities import ASYNC_OPERATIONS
2730from morango .constants .capabilities import FSIC_V2_FORMAT
31+ from morango .constants .capabilities import SELF_REF_ORDER
2832from morango .errors import MorangoDirtyParent
2933from morango .errors import MorangoInvalidFSICPartition
3034from morango .errors import MorangoLimitExceeded
@@ -525,7 +529,7 @@ def _queue_into_buffer_v1(transfersession):
525529 """SELECT
526530 id, serialized, deleted, last_saved_instance, last_saved_counter, hard_deleted, model_name, profile,
527531 partition, source_id, conflicting_serialized_data,
528- CAST ('{transfer_session_id}' AS {transfer_session_id_type}), _self_ref_fk
532+ CAST ('{transfer_session_id}' AS {transfer_session_id_type}), _self_ref_fk, _self_ref_order
529533 FROM {store} WHERE {condition}
530534 """ .format (
531535 transfer_session_id = transfersession .id ,
@@ -556,7 +560,7 @@ def _queue_into_buffer_v1(transfersession):
556560 """INSERT INTO {outgoing_buffer}
557561 (model_uuid, serialized, deleted, last_saved_instance, last_saved_counter,
558562 hard_deleted, model_name, profile, partition, source_id, conflicting_serialized_data,
559- transfer_session_id, _self_ref_fk)
563+ transfer_session_id, _self_ref_fk, _self_ref_order )
560564 {select}
561565 """ .format (
562566 outgoing_buffer = Buffer ._meta .db_table ,
@@ -674,7 +678,7 @@ def _queue_into_buffer_v2(transfersession, chunk_size=200):
674678 """SELECT
675679 id, serialized, deleted, last_saved_instance, last_saved_counter, hard_deleted, model_name, profile,
676680 partition, source_id, conflicting_serialized_data,
677- CAST ('{transfer_session_id}' AS {transfer_session_id_type}), _self_ref_fk
681+ CAST ('{transfer_session_id}' AS {transfer_session_id_type}), _self_ref_fk, _self_ref_order
678682 FROM {store} WHERE {condition}
679683 """ .format (
680684 transfer_session_id = transfersession .id ,
@@ -703,7 +707,7 @@ def _queue_into_buffer_v2(transfersession, chunk_size=200):
703707 """INSERT INTO {outgoing_buffer}
704708 (model_uuid, serialized, deleted, last_saved_instance, last_saved_counter,
705709 hard_deleted, model_name, profile, partition, source_id, conflicting_serialized_data,
706- transfer_session_id, _self_ref_fk)
710+ transfer_session_id, _self_ref_fk, _self_ref_order )
707711 {select}
708712 """ .format (
709713 outgoing_buffer = Buffer ._meta .db_table ,
@@ -721,7 +725,41 @@ def _queue_into_buffer_v2(transfersession, chunk_size=200):
721725 )
722726
723727
724- def _dequeue_into_store (transfer_session , fsic , v2_format = False ):
728+ def _update_legacy_self_ref_order_for_model (queryset ):
729+ # root nodes set the _self_ref_order to 0
730+ queryset .filter (_self_ref_fk = "" ).exclude (_self_ref_order = 0 ).update (_self_ref_order = 0 )
731+ # reset the _self_ref_order to None for all records that have a parent
732+ queryset .exclude (_self_ref_fk = "" ).exclude (_self_ref_order = None ).update (
733+ _self_ref_order = None
734+ )
735+
736+ parent = Store .objects .filter (
737+ id = Cast (OuterRef ("_self_ref_fk" ), UUIDField ()),
738+ _self_ref_order__isnull = False ,
739+ )
740+ parent_order = parent .values ("_self_ref_order" )[:1 ]
741+ pending = queryset .exclude (_self_ref_fk = "" ).filter (_self_ref_order = None )
742+
743+ while pending .filter (Exists (parent )).update (_self_ref_order = Subquery (parent_order ) + 1 ):
744+ pass
745+
746+
747+ def _update_legacy_self_ref_order (transfer_session ):
748+ profile = transfer_session .sync_session .profile
749+ transferred_store_records = Store .objects .filter (
750+ last_transfer_session_id = transfer_session .id ,
751+ profile = profile ,
752+ )
753+
754+ for Model in syncable_models .get_models (profile ):
755+ queryset = transferred_store_records .filter (model_name = Model .morango_model_name )
756+ if self_referential_fk (Model ):
757+ _update_legacy_self_ref_order_for_model (queryset )
758+ else :
759+ queryset .exclude (_self_ref_order = None ).update (_self_ref_order = None )
760+
761+
762+ def _dequeue_into_store (transfer_session , fsic , v2_format = False , self_ref_order = True ):
725763 """
726764 Takes data from the buffers and merges into the store and record max counters.
727765
@@ -745,6 +783,8 @@ def _dequeue_into_store(transfer_session, fsic, v2_format=False):
745783 DBBackend ._dequeuing_delete_mc_buffer (cursor , transfer_session .id )
746784 DBBackend ._dequeuing_insert_remaining_buffer (cursor , transfer_session .id )
747785 DBBackend ._dequeuing_insert_remaining_rmcb (cursor , transfer_session .id )
786+ if not self_ref_order :
787+ _update_legacy_self_ref_order (transfer_session )
748788 DBBackend ._dequeuing_delete_remaining_rmcb (cursor , transfer_session .id )
749789 DBBackend ._dequeuing_delete_remaining_buffer (cursor , transfer_session .id )
750790
@@ -1083,6 +1123,7 @@ def handle(self, context):
10831123 context .transfer_session ,
10841124 fsic ,
10851125 v2_format = FSIC_V2_FORMAT in context .capabilities ,
1126+ self_ref_order = SELF_REF_ORDER in context .capabilities ,
10861127 )
10871128
10881129 return transfer_statuses .COMPLETED
0 commit comments