Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Cet article décrit les méthodes disponibles pour interroger des données Dataverse à l’aide du Kit de développement logiciel (SDK) pour Python. Vous pouvez interroger des données à l’aide de SQL (Structured Query Language) et d’API OData.
Python développeurs doivent d’abord en savoir plus sur le Kit de développement logiciel (SDK) pour Python en lisant Émarrant avant de continuer avec cet article.
QueryBuilder
QueryBuilder est la méthode recommandée pour interroger les enregistrements. Il fournit une interface fluide et à typage sûr qui génère automatiquement des requêtes OData correctes. Vous n’avez pas besoin de mémoriser la syntaxe du filtre OData.
# Fluent query builder (recommended)
from PowerPlatform.Dataverse.models.filters import col
for record in (client.query.builder("account")
.select("name", "revenue")
.where(col("statecode") == 0)
.where(col("revenue") > 1000000)
.order_by("revenue", descending=True)
.top(100)
.page_size(50)
.execute()):
print(f"{record['name']}: {record['revenue']}")
QueryBuilder gère automatiquement la mise en forme des valeurs, la casse de nom de colonne et la syntaxe OData. Générez des expressions de filtre avec col() et des opérateurs de Python standard.
# Get results as a pandas DataFrame (consolidates all pages)
df = (client.query.builder("account")
.select("name", "telephone1")
.where(col("statecode") == 0)
.top(100)
.execute()
.to_dataframe())
print(f"Got {len(df)} accounts")
# Comparison filters using col() expressions
query = (client.query.builder("contact")
.where(col("statecode") == 0) # statecode eq 0
.where(col("revenue") > 1000000) # revenue gt 1000000
.where(col("name").contains("Corp")) # contains(name, 'Corp')
.where(col("statecode").in_([0, 1])) # Microsoft.Dynamics.CRM.In(...)
.where(col("revenue").between(100000, 500000)) # revenue ge 100000 and revenue le 500000
.where(col("telephone1").is_null()) # telephone1 eq null
)
Pour une logique complexe (OR, NOT, regroupement), composez des expressions avec &, |, ~:
from PowerPlatform.Dataverse.models.filters import col
# OR conditions: (statecode = 0 OR statecode = 1) AND revenue > 100k
for record in (client.query.builder("account")
.select("name", "revenue")
.where(((col("statecode") == 0) | (col("statecode") == 1))
& (col("revenue") > 100000))
.execute()):
print(record["name"])
# NOT, between, and in operators
for record in (client.query.builder("account")
.where(col("statecode") != 2) # NOT inactive
.where(col("revenue").between(100000, 500000)) # revenue in range
.execute()):
print(record["name"])
Valeurs et annotations mises en forme
Cet exemple montre comment demander des étiquettes localisées, des symboles monétaires et des noms d’affichage.
# Get formatted values (choice labels, currency, lookup names) — via query builder
for record in (client.query.builder("account")
.select("name", "statecode", "revenue")
.include_formatted_values()
.execute()):
status = record["statecode@OData.Community.Display.V1.FormattedValue"]
print(f"{record['name']}: {status}")
# Get formatted values — via records.list() / records.retrieve() include_annotations param
result = client.records.list(
"account",
select=["name", "statecode"],
include_annotations="OData.Community.Display.V1.FormattedValue",
)
for record in result:
label = record.get("statecode@OData.Community.Display.V1.FormattedValue")
print(f"{record['name']}: {label}")
record = client.records.retrieve(
"account", account_id,
select=["name", "statuscode"],
include_annotations="OData.Community.Display.V1.FormattedValue",
)
if record:
print(record.get("statuscode@OData.Community.Display.V1.FormattedValue"))
Développer les propriétés de navigation
Utilisez le développement imbriqué avec des options pour développer les propriétés de navigation en utilisant $select, $filter, $orderby et $top.
from PowerPlatform.Dataverse.models.query_builder import ExpandOption
# Expand related tasks with filtering and sorting
for record in (client.query.builder("account")
.select("name")
.expand(ExpandOption("Account_Tasks")
.select("subject", "createdon")
.filter("contains(subject,'Task')")
.order_by("createdon", descending=True)
.top(5))
.execute()):
print(record["name"], record.get("Account_Tasks"))
Paging
Utilisez execute_pages() pour traiter en continu des jeux de résultats volumineux avec toutes les options du générateur, telles que le filtrage, le tri et les valeurs formatées. Pour simplifier les requêtes de filtre OData basées sur des chaînes, utilisez records.list() et records.list_pages() en tant que raccourcis.
# Preferred: query.builder().execute_pages() — stream one page at a time, memory stays flat
# Supports composable filters, sorting, formatted values, and expand with nested selects
for page_num, page in enumerate(
client.query.builder("account")
.select("accountid", "name", "revenue")
.where(col("statecode") == 0)
.order_by("name")
.page_size(500) # optional: override Dataverse default (~5000/page)
.execute_pages()
):
print(f"Page {page_num + 1}: {len(page)} records")
for record in page:
print(f" {record['name']}")
# Simple shortcut: records.list() — automatic paging, all records in memory
# Use for basic filter+select queries; string OData filter only (no composable expressions)
result = client.records.list(
"account",
filter="statecode eq 0",
select=["name", "revenue"],
orderby=["name asc"], # optional sort
top=500, # bounds total records returned and number of HTTP round-trips
page_size=200, # optional: hint Dataverse default page size
)
for record in result:
print(record["name"])
# Simple streaming shortcut: records.list_pages() — same params as records.list(), yields one page at a time
for page_num, page in enumerate(
client.records.list_pages("account", filter="statecode eq 0", select=["name"], orderby=["name asc"])
):
print(f"Page {page_num + 1}: {len(page)} records")
for record in page:
print(record["name"])
Note
execute(by_page=True) et execute(by_page=False) sont tous deux obsolètes et émettent un UserWarning. Remplacez-les par execute_pages() (diffusion en continu) ou par execute() simple (immédiat).
QueryBuilder.to_dataframe() est également déconseillé : utilisez .execute().to_dataframe() à la place.
L’outil de migration réécrit automatiquement tous ces appels. Installez l’outil de migration en exécutant pip install PowerPlatform-Dataverse-Client[migration], puis exécutez dataverse-migrate path/to/your/scripts/. Vous pouvez également exécuter python -m PowerPlatform.Dataverse.migration.migrate_v0_to_v1 pour les extractions de développement.
Nombre d’enregistrements
Pour obtenir un nombre d’enregistrements, incluez-le $count=true dans la requête.
# Via query builder
results = (client.query.builder("account")
.where(col("statecode") == 0)
.count()
.execute())
# Via records.list() — count=True adds $count=true to the OData request
results = client.records.list("account", filter="statecode eq 0", count=True)
Requêtes FetchXML
L’appel client.query.fetchxml() retourne un objet inert FetchXmlQuery . Aucune requête HTTP n’est effectuée jusqu’à ce que vous appeliez .execute() ou .execute_pages().
xml = """
<fetch>
<entity name="account">
<attribute name="name"/>
<attribute name="revenue"/>
<filter><condition attribute="statecode" operator="eq" value="0"/></filter>
</entity>
</fetch>
"""
# .execute() — blocking, fetches all pages and returns a single QueryResult
result = client.query.fetchxml(xml).execute()
df = result.to_dataframe()
# .execute_pages() — streaming, yields one QueryResult per HTTP page
# Use count="N" in the FetchXML <fetch> element to set page size
for page_num, page in enumerate(client.query.fetchxml(xml).execute_pages()):
print(f"Page {page_num + 1}: {len(page)} records")
for record in page:
print(record["name"])
Raccourci de liste simple
L’appel records.list() accepte une chaîne de filtre OData brute pour les requêtes de base. Pour tout ce qui va au-delà d’un simple filtre + sélection, préférez utiliser client.query.builder(), qui offre des filtres composables, des valeurs mises en forme et un développement imbriqué.
# records.list() shortcut — raw OData filter string, all records loaded into memory
# Column names in filter must be lowercase logical names
for record in client.records.list(
"account",
select=["name"],
filter="statecode eq 0",
top=100,
):
print(record["name"])
# Discover navigation property names for $expand (metadata-discovery helper, kept at GA)
nav_props = client.query.odata_expands("account") # → list of navigation property metadata
# Expand navigation properties using the query builder
from PowerPlatform.Dataverse.models.query_builder import ExpandOption
for record in (client.query.builder("contact")
.select("fullname")
.expand(ExpandOption("parentcustomerid_account").select("name"))
.execute()):
acct = record.get("parentcustomerid_account") or {}
print(f"{record['fullname']} -> {acct.get('name')}")
# Build @odata.bind for lookup fields (deprecated helper, still functional with DeprecationWarning)
bind = client.query.odata_bind("contact", "account", account_id)
# Returns: {"parentcustomerid_account@odata.bind": "/accounts(guid)"}
client.records.create("contact", {"firstname": "Jane", **bind})
Interroger des données à l’aide de SQL
Dataverse fournit une interface en lecture seule à un ensemble limité de commandes SQL SELECT . La prise en charge des jointures SQL, des agrégats, de GROUP BY, de DISTINCT et de la pagination OFFSET FETCH est assurée.
Vous pouvez également accéder à la fonctionnalité de requête SQL à l'aide du paramètre ?sql= de l'API Web Dataverse afin que le code écrit dans des langages autres que Python puisse accéder aux données Dataverse. Pour plus d’informations, consultez Utiliser SQL pour interroger des données avec l’API web Dataverse.
Important
La prise en charge de SQL est limitée aux requêtes en lecture seule. Les jointures complexes, les sous-requêtes et certaines fonctions SQL peuvent ne pas être prises en charge. La requête SQL doit suivre le sous-ensemble pris en charge :
- WHERE ne peut être qu’une arborescence d’expressions booléennes où les feuilles sont des opérateurs binaires (=, >comme, etc.) avec l’un des arguments étant une référence de colonne directe et une autre est une constante
- TOP autorise uniquement un littéral entier
- ORDERBY ne peut référencer que des colonnes et n’autorise aucune expression complexe
L’exemple de code suivant illustre une requête SQL en Python.
# Basic query
results = client.query.sql(
"SELECT TOP 10 accountid, name FROM account WHERE statecode = 0"
)
# JOINs and aggregates work
results = client.query.sql(
"SELECT a.name, COUNT(c.contactid) as cnt "
"FROM account a "
"JOIN contact c ON a.accountid = c.parentcustomerid "
"GROUP BY a.name"
)
# SQL results directly as a DataFrame
df = client.dataframe.sql(
"SELECT name, revenue FROM account ORDER BY revenue DESC"
)
# Discover columns from metadata (schema-discovery helper, kept at GA)
cols_meta = client.query.sql_columns("account")
col_names = [c["LogicalName"] for c in cols_meta]
# Build queries using the discovered column names
sql = f"SELECT TOP 10 {', '.join(col_names[:5])} FROM account"
df = client.dataframe.sql(sql)