Hi there! I’m updating my API calls in Python to meet the requirements for v4. I’m by no means an expert in Python so I’m not sure how to deal with this.
My code:
configuration = asana.Configuration()
configuration.access_token = '<PAT>'
project_id = "<redacted>"
tasks_call = asana.TasksApi(asana.ApiClient(configuration))
tasks_call_opts = ["name","assignee.name","completed","memberships.section.name"]
tasks_data = tasks_call.get_tasks_for_project(project_id, opt_fields=tasks_call_opts)
...
for c, x in enumerate(tasks_data):
#some actions
When I run this, it returns TypeError: 'TaskResponseArray' object is not iterable. This worked in v3, so I’m not sure what changed with the API response. Thanks!
TLDR; to make the iteration work call tasks_data.data.
The v4 version of the python client libraries does not auto abstract the data property from the response. This can be confusing since the class name of the response is asana.models.task_response_array.TaskResponseArray. The reason for not abstracting the data wrapper is because data and next_page live at the same level so in scenarios where there is a next page if we return the content of data developers wound’t be able to access .next_page from the response i.e.,:
{
"data": {
...
},
"next_page": {
...
}
}
NOTE:next_page is only present in the response when a limit is provided in the query params or the request or when there is a lot of data to be returned.
Of course we could reconsider this design depending on developer feedback. However, for now I believe all of the responses from the v4 python client libraries have the response data wrapped in a data wrapper so you must call .data in order to access the data inside of that wrapper. This is consistent with our docs:
configuration = asana.Configuration()
configuration.access_token = '<PAT>'
project_id = "<redacted>"
tasks_call = asana.TasksApi(asana.ApiClient(configuration))
tasks_call_opts = ["name","assignee.name","completed","memberships.section.name"]
tasks_data = tasks_call.get_tasks_for_project(project_id, opt_fields=tasks_call_opts)
...
for c, x in enumerate(tasks_data.data):
#some actions
If you are curious, our new libraries are auto generated from a code generator (swagger codegen) using our SDK OpenAPI Spec. Here is the location of the spec where TaskResponseArray is being defined. TaskResponseArray is the name of the schema that Get tasks from a project endpoint references in our OpenAPI Spec.
This is a great response! Thank you so much for the clarification. Makes total sense.
The issue for me now is that the data is no longer “subscriptable”. e.g. this from another section of code added after I wrote this initial post. Here, I’m looking to pull status updates and get the title of the most-recent one:
configuration = asana.Configuration()
configuration.access_token = '<PAT>'
project_id = "<redacted>"
updates_call = asana.StatusUpdatesApi(asana.ApiClient(configuration))
updates_call_opts = ["status_type","text","title"]
updates_data = updates_call.get_statuses_for_object(project_id, opt_fields=updates_call_opts)
updates_data = updates_data.data
for c, x in enumerate(updates_data):
if c == 0:
update_text = x["text"]
update_title = x["title"]
exit()
The error now is
Traceback (most recent call last):
File "script.py", line 63, in <module>
update_text = x["text"]
TypeError: 'StatusUpdateResponse' object is not subscriptable
(I can use a more similar example if necessary but I think the issue would be the same.)
The responses in the v4 python client libraries return an object of that resource . In this scenario the response object is <class 'asana.models.status_update_response_array.StatusUpdateResponseArray'>. In order to access properties of this response you can use the dot notation so for example using your example:
configuration = asana.Configuration()
configuration.access_token = '<PAT>'
project_id = "<redacted>"
updates_call = asana.StatusUpdatesApi(asana.ApiClient(configuration))
updates_call_opts = ["status_type","text","title"]
updates_data = updates_call.get_statuses_for_object(project_id, opt_fields=updates_call_opts) # -> StatusUpdateResponseArray
updates_data = updates_data.data # -> list of StatusUpdateResponse
for c, x in enumerate(updates_data):
if c == 0:
update_text = x.text
update_title = x.title
exit()
If you would like to use bracket notation you would need to first convert the response into a dict:
configuration = asana.Configuration()
configuration.access_token = '<PAT>'
project_id = "<redacted>"
updates_call = asana.StatusUpdatesApi(asana.ApiClient(configuration))
updates_call_opts = ["status_type","text","title"]
updates_data = updates_call.get_statuses_for_object(project_id, opt_fields=updates_call_opts)
updates_data_dict = updates_data.to_dict() # -> dict
for c, x in enumerate(updates_data_dict["data"]):
if c == 0:
update_text = x["text"]
update_title = x["title"]
exit()