nodes: 0, module; 1, function_definition; 2, function_name:set_orders; 3, parameters; 4, block; 5, identifier:self; 6, identifier:object_pks; 7, expression_statement; 8, expression_statement; 9, expression_statement; 10, comment:# Call list() on the values right away, so they don't get affected by the; 11, comment:# update() later (since values_list() is lazy).; 12, expression_statement; 13, comment:# Check there are no unrecognised entries in the object_pks list. If so,; 14, comment:# throw an error. We only have to check that they're the same length because; 15, comment:# orders is built using only entries in object_pks, and all the pks are unique,; 16, comment:# so if their lengths are the same, the elements must match up exactly.; 17, if_statement; 18, with_statement; 19, comment:# Return the operated-on queryset for convenience.; 20, return_statement; 21, comment:"""
          Perform a mass update of sort_orders across the full queryset.
          Accepts a list, object_pks, of the intended order for the objects.
  
          Works as follows:
          - Compile a list of all sort orders in the queryset. Leave out anything that
            isn't in the object_pks list - this deals with pagination and any
            inconsistencies.
          - Get the maximum among all model object sort orders. Update the queryset to add
            it to all the existing sort order values. This lifts them 'out of the way' of
            unique_together clashes when setting the intended sort orders.
          - Set the sort order on each object. Use only sort_order values that the objects
            had before calling this method, so they get rearranged in place.
          Performs O(n) queries.
          """; 22, assignment; 23, assignment; 24, assignment; 25, comparison_operator:len(orders) != len(object_pks); 26, block; 27, with_clause; 28, block; 29, identifier:objects_to_sort; 30, identifier:objects_to_sort; 31, call; 32, identifier:max_value; 33, subscript; 34, identifier:orders; 35, call; 36, call; 37, call; 38, expression_statement; 39, expression_statement; 40, raise_statement; 41, with_item; 42, expression_statement; 43, for_statement; 44, attribute; 45, argument_list; 46, call; 47, string; 48, identifier:list; 49, argument_list; 50, identifier:len; 51, argument_list; 52, identifier:len; 53, argument_list; 54, assignment; 55, assignment; 56, call; 57, call; 58, call; 59, pattern_list; 60, call; 61, comment:# Use update() to save a query per item and dodge the insertion sort; 62, comment:# code in save().; 63, block; 64, identifier:self; 65, identifier:filter; 66, keyword_argument; 67, attribute; 68, argument_list; 69, string_content:sort_order__max; 70, call; 71, identifier:orders; 72, identifier:object_pks; 73, identifier:pks; 74, call; 75, identifier:message; 76, call; 77, identifier:TypeError; 78, argument_list; 79, attribute; 80, argument_list; 81, attribute; 82, argument_list; 83, identifier:pk; 84, identifier:order; 85, identifier:zip; 86, argument_list; 87, expression_statement; 88, identifier:pk__in; 89, identifier:object_pks; 90, call; 91, identifier:aggregate; 92, call; 93, attribute; 94, argument_list; 95, identifier:set; 96, argument_list; 97, attribute; 98, argument_list; 99, identifier:message; 100, identifier:transaction; 101, identifier:atomic; 102, identifier:objects_to_sort; 103, identifier:update; 104, keyword_argument; 105, identifier:object_pks; 106, identifier:orders; 107, call; 108, attribute; 109, argument_list; 110, attribute; 111, argument_list; 112, identifier:objects_to_sort; 113, identifier:values_list; 114, string; 115, keyword_argument; 116, call; 117, string; 118, identifier:format; 119, list_comprehension; 120, identifier:sort_order; 121, binary_operator:models.F('sort_order') + max_value; 122, attribute; 123, argument_list; 124, attribute; 125, identifier:all; 126, identifier:models; 127, identifier:Max; 128, string; 129, string_content:sort_order; 130, identifier:flat; 131, True; 132, attribute; 133, argument_list; 134, string_content:The following object_pks are not in this queryset: {}; 135, identifier:pk; 136, for_in_clause; 137, if_clause; 138, call; 139, identifier:max_value; 140, call; 141, identifier:update; 142, keyword_argument; 143, attribute; 144, identifier:objects; 145, string_content:sort_order; 146, identifier:objects_to_sort; 147, identifier:values_list; 148, string; 149, keyword_argument; 150, identifier:pk; 151, identifier:object_pks; 152, comparison_operator:pk not in pks; 153, attribute; 154, argument_list; 155, attribute; 156, argument_list; 157, identifier:sort_order; 158, identifier:order; 159, identifier:self; 160, identifier:model; 161, string_content:pk; 162, identifier:flat; 163, True; 164, identifier:pk; 165, identifier:pks; 166, identifier:models; 167, identifier:F; 168, string; 169, identifier:self; 170, identifier:filter; 171, keyword_argument; 172, string_content:sort_order; 173, identifier:pk; 174, identifier:pk
code: def set_orders(self, object_pks):
          """
          Perform a mass update of sort_orders across the full queryset.
          Accepts a list, object_pks, of the intended order for the objects.
  
          Works as follows:
          - Compile a list of all sort orders in the queryset. Leave out anything that
            isn't in the object_pks list - this deals with pagination and any
            inconsistencies.
          - Get the maximum among all model object sort orders. Update the queryset to add
            it to all the existing sort order values. This lifts them 'out of the way' of
            unique_together clashes when setting the intended sort orders.
          - Set the sort order on each object. Use only sort_order values that the objects
            had before calling this method, so they get rearranged in place.
          Performs O(n) queries.
          """
          objects_to_sort = self.filter(pk__in=object_pks)
          max_value = self.model.objects.all().aggregate(
              models.Max('sort_order')
          )['sort_order__max']
  
          # Call list() on the values right away, so they don't get affected by the
          # update() later (since values_list() is lazy).
          orders = list(objects_to_sort.values_list('sort_order', flat=True))
  
          # Check there are no unrecognised entries in the object_pks list. If so,
          # throw an error. We only have to check that they're the same length because
          # orders is built using only entries in object_pks, and all the pks are unique,
          # so if their lengths are the same, the elements must match up exactly.
          if len(orders) != len(object_pks):
              pks = set(objects_to_sort.values_list('pk', flat=True))
              message = 'The following object_pks are not in this queryset: {}'.format(
                  [pk for pk in object_pks if pk not in pks]
              )
              raise TypeError(message)
  
          with transaction.atomic():
              objects_to_sort.update(sort_order=models.F('sort_order') + max_value)
              for pk, order in zip(object_pks, orders):
                  # Use update() to save a query per item and dodge the insertion sort
                  # code in save().
                  self.filter(pk=pk).update(sort_order=order)
  
          # Return the operated-on queryset for convenience.
          return objects_to_sort