使用Python获取Azure DevOps用户故事关联工作项时MODE关键字错误如何解决?
解决Azure DevOps WIQL查询中MODE(Recursive)的报错问题
问题场景
使用Python的Azure DevOps模块,想要以树形结构(父-子链接)获取指定用户故事的关联工作项,原代码中使用了包含MODE (Recursive)的WIQL查询,但执行wit_client.query_by_wiql(wiql=wiql_query)时触发错误:
Exception has occurred: AzureDevOpsServiceError
The MODE keyword can not be used inside work item query. The error is caused by «Recursive»
原代码如下:
from azure.devops.connection import Connection from msrest.authentication import BasicAuthentication from azure.devops.v7_1.work_item_tracking.models import Wiql from variable import personal_access_token,org,project # Replace with your personal access token and organization URL personal_access_token = personal_access_token organization_url = org project_name = project # Create a connection to the Azure DevOps organization credentials = BasicAuthentication('', personal_access_token) connection = Connection(base_url=organization_url, creds=credentials) # Get the work item tracking client wit_client = connection.clients.get_work_item_tracking_client() # Define a WIQL (Work Item Query Language) query to get work items wiql_query = Wiql( query=f""" SELECT [System.Id], [System.WorkItemType], [System.Title], [System.AssignedTo], [System.State], [System.Tags] FROM workitems WHERE ( [Source].[System.TeamProject] = 'DevOps RnD' AND [Source].[System.Id] = 326797 ) AND ( [System.Links.LinkType] = 'System.LinkTypes.Hierarchy-Forward' ) ORDER BY [System.Id] MODE (Recursive) """ ) wiql_result = wit_client.query_by_wiql(wiql=wiql_query)
错误原因
MODE (Recursive)是Azure DevOps网页端查询树状工作项的专属语法,不支持通过Work Item Tracking API执行的WIQL语句,API层面需要用其他方式实现递归获取层级工作项。
解决方案
方法一:递归获取树形结构(推荐)
通过获取根工作项的关联关系,递归遍历所有子项,直接构建树形结构:
from azure.devops.connection import Connection from msrest.authentication import BasicAuthentication from variable import personal_access_token, org, project import json # 初始化Azure DevOps连接 credentials = BasicAuthentication('', personal_access_token) connection = Connection(base_url=org, creds=credentials) wit_client = connection.clients.get_work_item_tracking_client() def build_work_item_hierarchy(work_item_id, project_name): # 获取当前工作项,展开关联关系 work_item = wit_client.get_work_item( work_item_id, expand='Relations', project=project_name, fields=['System.Id', 'System.WorkItemType', 'System.Title', 'System.State', 'System.AssignedTo'] ) # 构建当前工作项的基础信息 item_data = { 'id': work_item.id, 'work_item_type': work_item.fields['System.WorkItemType'], 'title': work_item.fields['System.Title'], 'state': work_item.fields['System.State'], 'assigned_to': work_item.fields.get('System.AssignedTo', {}).get('displayName', '未分配'), 'children': [] } # 遍历关联关系,找到所有子项(Hierarchy-Forward链接) if hasattr(work_item, 'relations'): for relation in work_item.relations: if relation.rel == 'System.LinkTypes.Hierarchy-Forward': # 从链接URL中提取子项ID child_id = int(relation.url.split('/')[-1]) # 递归获取子项的层级结构 child_data = build_work_item_hierarchy(child_id, project_name) item_data['children'].append(child_data) return item_data # 调用函数获取树形结构 root_item_id = 326797 target_project = 'DevOps RnD' hierarchy_result = build_work_item_hierarchy(root_item_id, target_project) # 格式化输出结果 print(json.dumps(hierarchy_result, indent=2, ensure_ascii=False))
方法二:WIQL批量获取后自行构建树形
通过WIQL一次性获取所有层级的工作项(包括根项和所有子项),再手动处理父子关系:
from azure.devops.connection import Connection from msrest.authentication import BasicAuthentication from azure.devops.v7_1.work_item_tracking.models import Wiql from variable import personal_access_token, org, project # 初始化连接 credentials = BasicAuthentication('', personal_access_token) connection = Connection(base_url=org, creds=credentials) wit_client = connection.clients.get_work_item_tracking_client() # 定义WIQL查询,获取根项及所有子项 wiql_query = Wiql( query=f""" SELECT [System.Id], [System.WorkItemType], [System.Title], [System.AssignedTo], [System.State], [System.Tags] FROM workitems WHERE [System.TeamProject] = 'DevOps RnD' AND ( [System.Id] = 326797 OR [System.Id] IN ( SELECT [System.Id] FROM workitems WHERE [System.Links.LinkType] = 'System.LinkTypes.Hierarchy-Reverse' AND [Source].[System.Id] = 326797 ) ) ORDER BY [System.Id] """ ) # 执行查询 wiql_result = wit_client.query_by_wiql(wiql=wiql_query) # 获取所有工作项详情 work_items = [] for item_ref in wiql_result.work_items: work_item = wit_client.get_work_item(item_ref.id, expand='Relations') work_items.append(work_item) # 手动构建树形结构(示例逻辑) def build_hierarchy_from_flat_list(work_items, root_id): item_map = {item.id: item for item in work_items} hierarchy = None for item in work_items: item_data = { 'id': item.id, 'title': item.fields['System.Title'], 'children': [] } # 查找父项 if hasattr(item, 'relations'): for relation in item.relations: if relation.rel == 'System.LinkTypes.Hierarchy-Reverse': parent_id = int(relation.url.split('/')[-1]) if parent_id in item_map: item_map[parent_id].children.append(item_data) # 定位根项 if item.id == root_id: hierarchy = item_data return hierarchy # 构建并输出树形结构 root_item_id = 326797 final_hierarchy = build_hierarchy_from_flat_list(work_items, root_item_id)
方法对比
- 方法一:逻辑直观,直接递归构建树形,适合层级较深的场景,每次获取单个工作项,API调用次数等于工作项总数。
- 方法二:通过一次WIQL查询获取所有项,API调用次数少,但需要额外处理父子关系,适合层级较浅的场景。
内容的提问来源于stack exchange,提问作者DEEPAK YADAV
相关产品推荐
相关产品推荐

