docs: def bulk_update(manager, model_objs, fields_to_update):
      """
      Bulk updates a list of model objects that are already saved.
  
      :type model_objs: list of :class:`Models<django:django.db.models.Model>`
      :param model_objs: A list of model objects that have been updated.
          fields_to_update: A list of fields to be updated. Only these fields will be updated
  
  
      :signals: Emits a post_bulk_operation signal when completed.
  
      Examples:
  
      .. code-block:: python
  
          # Create a couple test models
          model_obj1 = TestModel.objects.create(int_field=1, float_field=2.0, char_field='Hi')
          model_obj2 = TestModel.objects.create(int_field=3, float_field=4.0, char_field='Hello')
  
          # Change their fields and do a bulk update
          model_obj1.int_field = 10
          model_obj1.float_field = 20.0
          model_obj2.int_field = 30
          model_obj2.float_field = 40.0
          bulk_update(TestModel.objects, [model_obj1, model_obj2], ['int_field', 'float_field'])
  
          # Reload the models and view their changes
          model_obj1 = TestModel.objects.get(id=model_obj1.id)
          print(model_obj1.int_field, model_obj1.float_field)
          10, 20.0
  
          model_obj2 = TestModel.objects.get(id=model_obj2.id)
          print(model_obj2.int_field, model_obj2.float_field)
          10, 20.0
  
      """
  
      # Add the pk to the value fields so we can join
      value_fields = [manager.model._meta.pk.attname] + fields_to_update
  
      # Build the row values
      row_values = [
          [_get_prepped_model_field(model_obj, field_name) for field_name in value_fields]
          for model_obj in model_objs
      ]
  
      # If we do not have any values or fields to update just return
      if len(row_values) == 0 or len(fields_to_update) == 0:
          return
  
      # Create a map of db types
      db_types = [
          manager.model._meta.get_field(field).db_type(connection)
          for field in value_fields
      ]
  
      # Build the value fields sql
      value_fields_sql = ', '.join(
          '"{field}"'.format(field=manager.model._meta.get_field(field).column)
          for field in value_fields
      )
  
      # Build the set sql
      update_fields_sql = ', '.join([
          '"{field}" = "new_values"."{field}"'.format(
              field=manager.model._meta.get_field(field).column
          )
          for field in fields_to_update
      ])
  
      # Build the values sql
      values_sql = ', '.join([
          '({0})'.format(
              ', '.join([
                  '%s::{0}'.format(
                      db_types[i]
                  ) if not row_number and i else '%s'
                  for i, _ in enumerate(row)
              ])
          )
          for row_number, row in enumerate(row_values)
      ])
  
      # Start building the query
      update_sql = (
          'UPDATE {table} '
          'SET {update_fields_sql} '
          'FROM (VALUES {values_sql}) AS new_values ({value_fields_sql}) '
          'WHERE "{table}"."{pk_field}" = "new_values"."{pk_field}"'
      ).format(
          table=manager.model._meta.db_table,
          pk_field=manager.model._meta.pk.column,
          update_fields_sql=update_fields_sql,
          values_sql=values_sql,
          value_fields_sql=value_fields_sql
      )
  
      # Combine all the row values
      update_sql_params = list(itertools.chain(*row_values))
  
      # Run the update query
      with connection.cursor() as cursor:
          cursor.execute(update_sql, update_sql_params)
  
      # call the bulk operation signal
      post_bulk_operation.send(sender=manager.model, model=manager.model)