@@ -669,50 +669,62 @@ def dict_if_none(obj, field_name=None):
669669
670670class ContentNodePagination (ValuesViewsetCursorPagination ):
671671 """
672- A simplified cursor pagination class for ContentNodeViewSet.
673- Instead of using an opaque cursor, it uses the lft value for filtering.
674- As such, if this pagination scheme is used without applying a filter
675- that will guarantee membership to a specific MPTT tree, such as parent
676- or tree_id, the pagination scheme will not be predictable.
672+ A simplified cursor pagination class
673+ Instead of using a fixed 'lft' cursor, it dynamically sets the pagination field and operator
674+ based on the incoming `ordering` query parameter.
677675 """
678- cursor_query_param = "lft__gt"
679- ordering = "lft"
680676 page_size_query_param = "max_results"
681677 max_page_size = 100
682678
679+ def get_pagination_params (self ):
680+ # Default ordering is "lft" if not provided.
681+ ordering_param = self .request .query_params .get ("ordering" , "lft" )
682+ # Remove the leading '-' if present to get the field name.
683+ pagination_field = ordering_param .lstrip ("-" )
684+ # Determine operator: if ordering starts with '-', use __lt; otherwise __gt.
685+ operator = "__lt" if ordering_param .startswith ("-" ) else "__gt"
686+ return pagination_field , operator
687+
683688 def decode_cursor (self , request ):
684689 """
685- Given a request with a cursor, return a `Cursor` instance.
690+ Given a request with a cursor parameter, return a `Cursor` instance.
691+ The cursor parameter name is dynamically built from the pagination field and operator.
686692 """
687- # Determine if we have a cursor, and if so then decode it.
688- value = request .query_params .get (self .cursor_query_param )
693+ pagination_field , operator = self .get_pagination_params ()
694+ cursor_param = f"{ pagination_field } { operator } "
695+ value = request .query_params .get (cursor_param )
689696 if value is None :
690697 return None
691-
692- try :
693- value = int (value )
694- except ValueError :
695- raise ValidationError ("lft must be an integer but an invalid value was given." )
698+ if pagination_field == "lft" :
699+ try :
700+ value = int (value )
701+ except ValueError :
702+ raise ValidationError ("lft must be an integer but an invalid value was given." )
696703
697704 return Cursor (offset = 0 , reverse = False , position = value )
698705
699706 def encode_cursor (self , cursor ):
700707 """
701- Given a Cursor instance, return an url with query parameter.
708+ Given a Cursor instance, return a URL with the dynamic pagination cursor query parameter.
702709 """
703- return replace_query_param (self .base_url , self .cursor_query_param , str (cursor .position ))
710+ pagination_field , operator = self .get_pagination_params ()
711+ cursor_param = f"{ pagination_field } { operator } "
712+ return replace_query_param (self .base_url , cursor_param , str (cursor .position ))
704713
705714 def get_more (self ):
715+ """
716+ Construct a "more" URL (or query parameters) that includes the pagination cursor
717+ built from the dynamic field and operator.
718+ """
719+ pagination_field , operator = self .get_pagination_params ()
720+ cursor_param = f"{ pagination_field } { operator } "
706721 position , offset = self ._get_more_position_offset ()
707722 if position is None and offset is None :
708723 return None
709724 params = self .request .query_params .copy ()
710- params .update ({
711- self .cursor_query_param : position ,
712- })
725+ params .update ({cursor_param : position })
713726 return params
714727
715-
716728# Apply mixin first to override ValuesViewset
717729class ContentNodeViewSet (BulkUpdateMixin , ValuesViewset ):
718730 queryset = ContentNode .objects .all ()
0 commit comments