Using the GSpread package for Python, how would I use a batch_update to delete a range of cells upwards?
Example code:
sheet_id = self.worksheet.gs_worksheet._properties['sheetId']
start_index_col = self.cell_data_by_row_col[0][0].col - 1
end_index_col = self.cell_data_by_row_col[0][-1].col - 1
start_index_row = self.cell_data_by_row_col[0][0].row
end_index_row = self.cell_data_by_row_col[0][0].row
self.worksheet.gs_worksheet.batch_update({
'requests': [
{
'deleteRangeRequest': {
'range': {
'sheetId': sheet_id,
'startRowIndex': start_index_row,
'endRowIndex': end_index_row,
'startColumnIndex': start_index_col,
'endColumnIndex': end_index_col,
},
'shiftDimension': 'ROWS',
}
}
]
})
Response:
File "C:\Program Files\Python37\lib\site-packages\gspread\utils.py", line 559, in wrapper
return f(*args, **kwargs)
File "C:\Program Files\Python37\lib\site-packages\gspread\models.py", line 1166, in batch_update
for vr in data
File "C:\Program Files\Python37\lib\site-packages\gspread\models.py", line 1166, in <listcomp>
for vr in data
TypeError: string indices must be integers
I believe your goal as follows.
You want to delete range using deleteRangeRequest of the method of "spreadsheets.batchUpdate" in Sheets API.
You want to achieve this using gspread with python.
Modification points:
At gspread, it seems that batch_update(body) is the method of class gspread.models.Spreadsheet. In your script, I thought that you might use it as the method of class gspread.models.Worksheet. I think that this is the reason of the error message of string indices must be integers.
batch_update(data, **kwargs) of class gspread.models.Worksheet is the method of "spreadsheets.values.batchUpdate".
When the DeleteRangeRequest is used in the "spreadsheets.batchUpdate" method, please use it as deleteRange.
When above point is reflected to the script, it becomes as follows. Unfortunately, from your script, I couldn't understand about the variable of self.worksheet.gs_worksheet. So in this modification, I use other variable name.
Modified script:
spreadsheetId = "###" # Please set the Spreadsheet Id.
sheetName = "Sheet1" # Please set the sheet name.
client = gspread.authorize(credentials)
spreadsheet = client.open_by_key(spreadsheetId)
sheet_id = spreadsheet.worksheet(sheetName)._properties['sheetId']
start_index_col = self.cell_data_by_row_col[0][0].col - 1
end_index_col = self.cell_data_by_row_col[0][-1].col - 1
start_index_row = self.cell_data_by_row_col[0][0].row
end_index_row = self.cell_data_by_row_col[0][0].row
spreadsheet.batch_update({
'requests': [
{
'deleteRange': {
'range': {
'sheetId': sheet_id,
'startRowIndex': start_index_row,
'endRowIndex': end_index_row,
'startColumnIndex': start_index_col,
'endColumnIndex': end_index_col,
},
'shiftDimension': 'ROWS',
}
}
]
})
References:
batch_update(body)
Method: spreadsheets.batchUpdate
DeleteRangeRequest
Method: spreadsheets.values.batchUpdate
Related
I am trying to write a 0 into a predefined range of cells in google sheets. Using gspread I am able to blank clear all the cells I want but I am unable to figure out how to batch update all the cells to one value. Iterating through the cells and updating them one by one is too slow. I'm sure its just a simple formatting error but I can't find documentation on the proper way
sheet.batch_clear(['A2:A50']) #works fine
sheet.batch_update(['B2:B50'], 0) #does not work
In your situation, I thought that when the batch_update method of Class Spreadsheet is used, your goal can be achieved by one API call. When this is reflected in a sample script using gspread, it becomes as follows.
Sample script:
client = gspread.authorize(credentials) # Please use your authorization script.
spreadsheetId = "###" # Please set your Spreadsheet ID.
sheetName = "Sheet1" # Please set your sheet name.
spreadsheet = client.open_by_key(spreadsheetId)
sheet_id = spreadsheet.worksheet(sheetName).id
requests = {
"requests": [
{
"repeatCell": {
"cell": {
"userEnteredValue": {
"numberValue": 0
}
},
"range": {
"sheetId": sheet_id,
"startRowIndex": 1,
"endRowIndex": 50,
"startColumnIndex": 1,
"endColumnIndex": 2
},
"fields": "userEnteredValue"
}
}
]
}
spreadsheet.batch_update(requests)
When this script is run, the value of 0 is put to the cells B2:B50.
In this script, the A1Notation of B2:B50 is used as the GridRange.
References:
batch_update(body)
RepeatCellRequest
GridRange
I've been trying to configure the cell horizontal alignment format of a google spreadsheet in python. I've read the original google developers information and have also checked other examples from people facing the same issue, but none of them applies to my example. The following code is the one I use to configure my spreadsheet request body. What I want to do is to create a new spreadsheet in which the horizontal alignment of all the cells is centered. As a result, when a user types anything in any cell, it will be centered automatically. Any tips?
spreadsheet_body = {
'properties': {
# spreadsheet name
'title': sheet_name,
"defaultFormat":{
"horizontalAlignment":'CENTER'
}
},
'sheets': [{
'properties': {
# worksheet name
'title': 'Φύλλο1',
'gridProperties': {
# row\column number
'rowCount': 100,
'columnCount': 20
},
},
'data': [{'rowData': [{'values': header_row}]}] # Added
}
]
}
request = service.spreadsheets().create(body=spreadsheet_body)
response = request.execute()
I believe your goal is as follows.
When a new Spreadsheet is created using the method of spreadsheets.create of Sheets API, you want to set the horizontal alignment of the cells.
You want to achieve this using googleapis with python.
Issue and workaround:
Unfortunately, "defaultFormat":{"horizontalAlignment":'CENTER'} of Spreadsheet property cannot be used. This is the current specification. This has already been mentioned ziganotschka's answer.
When I saw your script, the created new Spreadsheet has one sheet. In this case, when this sheet is created, the horizontal alignment of all cells in the sheet can be set as CENTER. But in this case, when a new sheet is created, the default format is used. Please be careful about this. So this answer is a workaround.
When this workaround is reflected in your script, it becomes as follows.
Modified script:
rowCount = 100
columnCount = 20
rowData = []
for r in range(1, rowCount):
temp = []
for c in range(0, columnCount):
temp.append({'userEnteredFormat': {'horizontalAlignment': 'CENTER'}})
rowData.append({'values': temp})
hlen = len(header_row)
if hlen < columnCount:
for c in range(0, columnCount - hlen):
header_row.append({'userEnteredFormat': {'horizontalAlignment': 'CENTER'}})
rowData.insert(0, {'values': header_row})
spreadsheet_body = {
'properties': {
# spreadsheet name
'title': sheet_name
},
'sheets': [{
'properties': {
# worksheet name
'title': 'Φύλλο1',
'gridProperties': {
# row\column number
'rowCount': rowCount,
'columnCount': columnCount
},
},
'data': [{'rowData': rowData}]
}]}
request = service.spreadsheets().create(body=spreadsheet_body)
response = request.execute()
Reference:
Method: spreadsheets.create
Under the resource SpreadsheetProperties it is specified:
defaultFormat
This field is read-only.
So unfortunately it is not possible to create with the Sheets API a spreadsheet where all cells are centered automatically - just like it is not possible to do it via the UI.
Getting the following error when trying to do a batch_update post to a google sheet. There are 5600 rows in the sheet I am trying to post to
('/home/xx/xxx/xx.csv', <Spreadsheet u'spreadsheet name' id:id#>, 'A5600')
Traceback (most recent call last):
File "xx.py", line 50, in <module>
pasteCsv(csvFile, sheet, cell)
File "xx.py", line 38, in pasteCsv
return sheet.batch_update(body)
File "/home/xx/.local/lib/python2.7/site-packages/gspread/models.py", line 146, in batch_update
'post', SPREADSHEET_BATCH_UPDATE_URL % self.id, json=body
File "/home/xx/.local/lib/python2.7/site-packages/gspread/client.py", line 73, in request
raise APIError(response)
gspread.exceptions.APIError: {u'status': u'INVALID_ARGUMENT', u'message': u'Invalid requests[0].pasteData: GridCoordinate.rowIndex[5599] is after last row in grid[999]', u'code': 400}
is there a way to change the grid from [999] to a higher number so that I am able to post the csv file contents?
Answer:
You can make a batch request to increase the number of rows in the sheet before you insert the CSV content.
Example using a Batch Request:
spreadsheetId = "your-spreadsheet-id"
sheetId = "sheet-id"
sh = client.open_by_key(spreadsheetId)
request = {
"requests": [
{
"insertDimension": {
"range": {
"sheetId": sheetId,
"dimension": "ROWS",
"startIndex": 999,
"endIndex": 5599
},
"inheritFromBefore": false
}
}
]
}
result = sh.batch_update(request)
You will need to change sheetId to be the gid of the sheet within the Spreadsheet you are updating.
Remember: rows and columns are 0-indexed, so inserting rows below row 1000 will mean having a startIndex of 999.
Example using gspread methods:
Alternatively in gspread you can directly use the gspread.models.Worksheet.add_rows() method:
sh = client.open_by_key(spreadsheetId)
ws = sh.get_worksheet(index)
ws.add_rows(4600)
References:
Row and Column Operations | Sheets API | Google Developers
Insert an empty row or column
API Reference - gspread 3.6.0 documentation - add_rows(rows)
Is there a method like worksheet.delete_row in gspread google-sheet?
I tried:
delete = sheet.range('A1:A1000')
for cell in delete:
cell.value = ""
sheet.update_cells(delete)
but that only delete all values, not column.
Can anybode help me?
Answer:
There is no method in gspread to delete an entire column, like Workbook.delete_row, however you can do this with a batch update.
Code sample:
spreadsheetId = "your-spreadsheet-id"
sheetId = "id-of-sheet-to-delete-column-from"
sh = client.open_by_key(spreadsheetId)
request = {
"requests": [
{
"deleteDimension": {
"range": {
"sheetId": sheetId,
"dimension": "COLUMNS",
"startIndex": 0,
"endIndex": 1
}
}
}
]
}
result = sh.batch_update(request)
This sample will delete column A, but make sure to change the startIndex and endIndex to be of the column range you wish to delete.
Edit:
If you do not know the sheetId of a given sheet, you can get it using the following:
sheetName = "theSheetName"
sheetId = sh.worksheet(sheetName)._properties["sheetId"]
Note that this is not needed for the original sheet of a Spreadsheet, as this will always be 0.
References:
Method: spreadsheets.batchUpdate | Sheets API | Google Developers
API References - gspread 3.4.0 documentation - batch_update(body)
Update 2020-04-15:
This script was merged with gspread master today from pull request #759 as method delete_column().
The method will be available in the next release v3.5.0.
A method for delete_columns() was also added as a parallel method to the existing delete_rows() from pull request #761.
What I'm trying to do is getting each column in Source worksheet and pasting them to Target_sheet in another worksheet, each pasting action should start from the third row though (Ex: A3:A, B3:B ...)
However I get error such as:
ata.values[1731]","description": "Invalid value at 'data.values[1731]' (type.googleapis.com/google.protobuf.ListValue), \"x23232x2x2x442x42x42x42\""
},
{
"field": "data.values[1732]",
"description": "Invalid value at 'data.values[1732]' (type.googleapis.com/google.protobuf.ListValue), \"x242x42x42x42x42x442x427\""
},
{
"field": "data.values[1733]",
"description": "Invalid value at 'data.values[1733]' (type.googleapis.com/google.protobuf.ListValue), \"x42x424242x42454555x56666\""
}
.
.
.
My code:
sh = client.open('Target')
sh.values_clear("Target_sheet!A3:J10000")
source = client.open('Source')
source_col_numbers = source.sheet1.col_count
i = 1
# creating a holder for the values in Source.sheet1
columns = {}
#getting the values in each column at the Source.sheet1
while i <= source_col_numbers:
columns[i] = list(filter(None , source.sheet1.col_values(i)))
i += 1
# will use this variable to iterate between columns in the Target.Target_sheet
charn=ord("A")
#updating the columns in the Target with values from Source
b=1
while b <= source_col_numbers:
sh.values_update(
"Target_sheet!"+chr(charn)+"3:"+chr(charn)
,
params={
'valueInputOption': 'USER_ENTERED'
} ,
body={
'values': columns[b]
}
)
charn+=1
b+=1
#carlesgg97 tried with get_value but still getting error I mentioned under your comment:
target_worksheet.values_clear("Target!A3:J10000")
source = client.open('Source')
source_col_numbers = source.sheet1.col_count
source_values=source.values_get('Sheet1!A:J')
last_column=chr(source_col_numbers)
target_worksheet.values_update(
"Target!A3:"+last_column ,
params={ 'valueInputOption': 'USER_ENTERED' },
body={ 'values': source_values }
)
Using col_values you obtain the values of the column, in a list. However, when using the values_update method, the body requires the "values" property to be a list of lists (see: col_values).
Further to that, I believe the task you are attempting to accomplish can be done in a much more simpler way, using values_get. An example that would move the range A1:J9997 to A4:J10000 (moved 3 rows down) would look as follows:
sh = client.open('Target')
sh.values_clear("Target_sheet!A3:J10000")
response = sh.values_get("A1:J9997")
spreadsheet.values_update(
'Target_sheet!A3:J10000',
params = {
'valueInputOption': 'USER_ENTERED'
},
body = {
'values': response['values']
}
)