Remarque
L’accès à cette page requiert une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page requiert une autorisation. Vous pouvez essayer de modifier des répertoires.
Cet article illustre un exemple de code qui utilise le Kit de développement logiciel (SDK) pour utiliser des données et des métadonnées Dataverse. Avant de continuer, veillez à lire Bien démarrer.
Opérations de base
Voici un exemple de code qui fonctionne sur la table de compte.
from azure.identity import InteractiveBrowserCredential
from PowerPlatform.Dataverse.client import DataverseClient
# Replace <myorg> with the name of a valid environment.
base_url = "https://<myorg>.crm.dynamics.com"
client = DataverseClient(base_url=base_url, credential=InteractiveBrowserCredential())
# Create a record
account_id = client.records.create("account", {"name": "Contoso Ltd"})
# Read a record
account = client.records.retrieve("account", account_id)
print(account["name"])
# Read with expand fetches a related record in the same HTTP request
account = client.records.retrieve(
"account", account_id,
select=["name"],
expand=["primarycontactid"],
)
contact = (account.get("primarycontactid") or {})
print(contact.get("fullname"))
# Update a record
client.records.update("account", account_id, {"telephone1": "555-0199"})
# Delete a record
client.records.delete("account", account_id)
Gestionnaire de contexte
Le Gestionnaire de contexte gère le nettoyage automatique et le regroupement de connexions HTTP. Utilisez la syntaxe suivante pour tirer parti du Gestionnaire de contexte.
with DataverseClient("https://<myorg>.crm.dynamics.com", credential) as client:
Le code de travail suivant montre comment utiliser le Gestionnaire de contextes.
from azure.identity import InteractiveBrowserCredential
from PowerPlatform.Dataverse.client import DataverseClient
# Connect to Dataverse
credential = InteractiveBrowserCredential()
with DataverseClient("https://<myorg>.crm.dynamics.com", credential) as client:
# Create a contact
contact_id = client.records.create("contact", {"firstname": "John", "lastname": "Doe"})
# Read the contact back
contact = client.records.retrieve("contact", contact_id, select=["firstname", "lastname"])
print(f"Created: {contact['firstname']} {contact['lastname']}")
# Clean up
client.records.delete("contact", contact_id)
# Session closed, caches cleared automatically
Opérations en bloc
Voici quelques exemples qui effectuent des opérations en bloc.
# Bulk create
payloads = [
{"name": "Company A"},
{"name": "Company B"},
{"name": "Company C"}
]
ids = client.records.create("account", payloads)
# Bulk update (broadcast same change to all)
client.records.update("account", ids, {"industry": "Technology"})
# Bulk delete
client.records.delete("account", ids, use_bulk_delete=True)
L’exemple suivant crée plusieurs comptes. Transmettez une liste de charges utiles à create(logical_name, payloads) pour appeler l'action liée à la collection Microsoft.Dynamics.CRM.CreateMultiple. La méthode retourne list[str] des ID d’enregistrement créés.
# Bulk create accounts (returns list of GUIDs)
payloads = [
{"name": "Contoso"},
{"name": "Fabrikam"},
{"name": "Northwind"},
]
ids = client.records.create("account", payloads)
assert isinstance(ids, list) and all(isinstance(x, str) for x in ids)
print({"created_ids": ids})
Pour plus d’informations sur les opérations en bloc :
-
updateretourneNonepour les appels à enregistrement unique et en bloc, afin de maintenir la cohérence sémantique. - Le mode diffusion ou par enregistrement est déterminé selon que le paramètre
changesest un dictionnaire ou une liste. - L’attribut de clé primaire est ajouté automatiquement lors de la création des cibles de l’action UpdateMultiple.
- Si une charge utile omet @odata.type, le SDK l’étiquette automatiquement (la recherche de nom logique mise en cache).
- La réponse inclut uniquement les ID : le KIT de développement logiciel (SDK) retourne ces chaînes GUID.
-
createretourne le GUID du nouvel enregistrement sous la forme d’unstrlorsque vous passez un seul dictionnaire, et sous la forme d’unlist[str]lorsque vous passez une liste de charges utiles. - La recherche
@odata.typede métadonnées est effectuée une fois par jeu d’entités (mis en cache en mémoire).
Upsert (création et mise à jour)
Une séquence d’accès aux données courante consiste à vérifier d’abord si une ligne de table existe. Si la ligne existe, mettez-la à jour. Sinon, créez la ligne. Vous pouvez rendre cette séquence plus efficace en utilisant un seul appel d’API de l’opération Upsert.
Pour plus d’informations, consultez Utiliser Upsert pour créer ou mettre à jour un enregistrement.
Important
La table doit avoir une autre clé configurée dans Dataverse pour les colonnes utilisées dans alternate_key. Définissez d'autres clés dans les métadonnées de la table via le portail Power Apps maker ou un appel d'API Dataverse. Sans clé secondaire configurée, Dataverse rejette les requêtes upsert avec une erreur 400.
Permet client.records.upsert() de créer ou de mettre à jour des enregistrements identifiés par d’autres clés. Lorsque la clé correspond à un enregistrement existant, la méthode met à jour l’enregistrement. Sinon, il crée l’enregistrement. Un seul élément utilise une requête PATCH tandis que plusieurs éléments utilisent l’action UpsertMultiple en bloc.
from PowerPlatform.Dataverse.models import UpsertItem
# Upsert a single record
client.records.upsert("account", [
UpsertItem(
alternate_key={"accountnumber": "ACC-001"},
record={"name": "Contoso Ltd", "telephone1": "555-0100"},
)
])
# Upsert multiple records (uses UpsertMultiple bulk action)
client.records.upsert("account", [
UpsertItem(
alternate_key={"accountnumber": "ACC-001"},
record={"name": "Contoso Ltd"},
),
UpsertItem(
alternate_key={"accountnumber": "ACC-002"},
record={"name": "Fabrikam Inc"},
),
])
# Composite alternate key (multiple columns identify the record)
client.records.upsert("account", [
UpsertItem(
alternate_key={"accountnumber": "ACC-001", "address1_postalcode": "98052"},
record={"name": "Contoso Ltd"},
)
])
# Plain dict syntax (no import needed)
client.records.upsert("account", [
{
"alternate_key": {"accountnumber": "ACC-001"},
"record": {"name": "Contoso Ltd"},
}
])
Cadres de données
Le SDK fournit des wrappers Pandas pour toutes les opérations CRUD via l’espace de noms client.dataframe. Ces wrappers utilisent les API DataFrame et Series de pandas pour les entrées et les sorties.
Note
client.dataframe.get() est déconseillé. Utilisez les modèles en disponibilité générale indiqués dans les sections suivantes.
import pandas as pd
from PowerPlatform.Dataverse.models import col
# Query records as a single DataFrame (GA builder pattern)
df = (client.query.builder("account")
.select("name", "telephone1")
.where(col("statecode") == 0)
.execute()
.to_dataframe())
print(f"Found {len(df)} accounts")
# Limit results with top for large tables
df = client.query.builder("account").select("name").top(100).execute().to_dataframe()
# Create records from a DataFrame (returns a Series of GUIDs)
new_accounts = pd.DataFrame([
{"name": "Contoso", "telephone1": "555-0100"},
{"name": "Fabrikam", "telephone1": "555-0200"},
])
new_accounts["accountid"] = client.dataframe.create("account", new_accounts)
# Update records from a DataFrame (id_column identifies the GUID column)
new_accounts["telephone1"] = ["555-0199", "555-0299"]
client.dataframe.update("account", new_accounts, id_column="accountid")
# Clear a field by setting clear_nulls=True (by default, NaN/None fields are skipped)
df = pd.DataFrame([{"accountid": new_accounts["accountid"].iloc[0], "websiteurl": None}])
client.dataframe.update("account", df, id_column="accountid", clear_nulls=True)
# Delete records by passing a Series of GUIDs
client.dataframe.delete("account", new_accounts["accountid"])
# SQL query directly to DataFrame (supports JOINs, aggregates, GROUP BY)
df = client.dataframe.sql(
"SELECT a.name, COUNT(c.contactid) as contacts "
"FROM account a "
"JOIN contact c ON a.accountid = c.parentcustomerid "
"GROUP BY a.name"
)
Charger des fichiers dans Dataverse
L’exemple suivant montre comment charger un fichier nommé document.pdf dans la colonne Fichier nommée new_Document d’un enregistrement de compte. Le Kit de développement logiciel (SDK) pour Python gère automatiquement la segmentation des fichiers pour les fichiers de plus de 128 Mo.
# Upload a file to a record
client.files.upload(
"account",
account_id,
"new_Document",
"/path/to/document.pdf",
)
Important
Si la colonne de fichier n’existe pas, le Kit de développement logiciel (SDK) le crée automatiquement. La création d’une colonne est une modification des métadonnées : vous avez besoin de privilèges pour personnaliser la table et la nouvelle colonne devient une partie permanente du schéma de votre environnement. Dans les environnements de production, créez des colonnes de fichiers à l’avance plutôt que de vous appuyer sur la création automatique.
La upload méthode accepte les paramètres facultatifs pour contrôler le type de contenu et la stratégie de transfert.
# Specify a MIME type (defaults to application/octet-stream)
client.files.upload(
"account", account_id, "new_Contract", "/path/to/contract.pdf",
mime_type="application/pdf",
)
# Force chunked transfer regardless of size
client.files.upload(
"email", email_id, "new_Attachment", "/path/to/large_file.zip",
mode="chunk",
)
# Overwrite an existing file (uploads fail by default when the column already holds a file)
client.files.upload(
"account", account_id, "new_Document", "/path/to/updated_contract.pdf",
if_none_match=False,
)
Le mode paramètre contrôle la façon dont le Kit de développement logiciel (SDK) transfère le fichier.
| Mode | Comportement |
|---|---|
"auto" (valeur par défaut) |
Sélectionne les "small" fichiers de moins de 128 Mo, ou "chunk" pour les fichiers de 128 Mo et plus. |
"small" |
Envoie l’intégralité du fichier dans une seule requête. Lève une exception ValueError si le fichier dépasse 128 Mo. |
"chunk" |
Diffuse le fichier dans des segments. Utiliser pour les fichiers volumineux ou forcer le transfert en bloc, quelle que soit la taille. |
Note
Les restrictions de taille de fichier dataverse s’appliquent aux chargements de fichiers (128 Mo par fichier par défaut). Le chargement d’un fichier plus volumineux avec mode="small" déclenche un ValueError. Utilisez mode="chunk" ou la valeur par défaut mode="auto". Le client asynchrone expose la même files.upload méthode. Appelez-le avec await.
Opérations par lots
Permet client.batch d’envoyer plusieurs opérations dans une requête HTTP. L’espace de noms batch reflète client.records, client.tableset client.query.
# Build a batch request and add operations
batch = client.batch.new()
batch.records.create("account", {"name": "Contoso"})
batch.records.create("account", [{"name": "Fabrikam"}, {"name": "Woodgrove"}])
batch.records.update("account", account_id, {"telephone1": "555-0100"})
batch.records.delete("account", old_id)
batch.records.retrieve("account", account_id, select=["name"], expand=["primarycontactid"]) # single record with expand
batch.records.list( # multi-record, single page
"account",
filter="statecode eq 0",
select=["name"],
orderby=["name asc"],
top=50,
)
result = batch.execute()
for item in result.responses:
if item.is_success:
print(f"[OK] {item.status_code} entity_id={item.entity_id}")
else:
print(f"[ERR] {item.status_code}: {item.error_message}")
Ensemble de modifications transactionnelles
Toutes les opérations d’un jeu de modifications réussissent ou sont annulées ensemble.
batch = client.batch.new()
with batch.changeset() as cs:
lead_ref = cs.records.create("lead", {"firstname": "Ada"})
contact_ref = cs.records.create("contact", {"firstname": "Ada"})
cs.records.create("account", {
"name": "Babbage & Co.",
"originatingleadid@odata.bind": lead_ref,
"primarycontactid@odata.bind": contact_ref,
})
result = batch.execute()
print(f"Created {len(result.entity_ids)} records atomically")
À l’intérieur d’un ensemble de modifications, cs.records.create() retourne une chaîne de référence content-ID telle que "$1". Utilisez-le dans le même jeu de modifications, sans attendre de réponse du serveur, soit comme valeur d’un champ @odata.bind (comme indiqué précédemment), soit comme argument record_id lors d’un appel ultérieur à cs.records.update() ou cs.records.delete(). Les opérations de lecture (retrieve, list) ne sont pas autorisées à l’intérieur d’un ensemble de modifications.
Métadonnées de table et requêtes SQL dans un lot
batch = client.batch.new()
batch.tables.create("new_Product", {"new_Price": "decimal", "new_InStock": "bool"})
batch.tables.add_columns("new_Product", {"new_Rating": "int"})
batch.tables.get("new_Product")
batch.query.sql("SELECT TOP 5 name FROM account")
result = batch.execute()
Continuer malgré l’erreur
Essayez toutes les opérations même en cas d’échec.
result = batch.execute(continue_on_error=True)
print(f"Succeeded: {len(result.succeeded)}, Failed: {len(result.failed)}")
for item in result.failed:
print(f"[ERR] {item.status_code}: {item.error_message}")
Intégration de DataFrame
Importez directement des DataFrames de pandas dans un lot.
import pandas as pd
batch = client.batch.new()
# Create records from a DataFrame
df = pd.DataFrame([{"name": "Contoso"}, {"name": "Fabrikam"}])
batch.dataframe.create("account", df)
# Update records from a DataFrame
updates = pd.DataFrame([
{"accountid": id1, "telephone1": "555-0100"},
{"accountid": id2, "telephone1": "555-0200"},
])
batch.dataframe.update("account", updates, id_column="accountid")
# Delete records from a Series
batch.dataframe.delete("account", pd.Series([id1, id2]))
result = batch.execute()
Gérer les réponses par lots et les limites
batch.execute() retourne un BatchResult. Sa responses liste contient une entrée par opération HTTP dans l’ordre de soumission. Les listes succeeded et failed sont des sous-ensembles de responses. La has_errors propriété est True en cas d’échec d’une opération. La liste entity_ids collecte les GUID à partir de l’en-tête OData-EntityId des créations et mises à jour réussies d’un seul enregistrement.
Chaque entrée de réponse expose status_code, entity_id, is_success, data (corps analysé pour les opérations de récupération, de liste et de requête), error_message et error_code. Différentes opérations retournent différentes formes :
-
Création d’un seul enregistrement — statut 204,
entity_idrenseigné. -
Création ou upsert en bloc : état 200, ID dans
item.data["Ids"](aucunOData-EntityIden-tête). -
Requête ou liste SQL : état 200, lignes dans
item.data["value"]. - Suppression ou écriture de métadonnées — statut 204, aucune donnée dans le corps.
Le nombre maximal d’opérations HTTP par lot est de 1 000. Si vous dépassez cette limite, le client génère une ValidationError avant d’envoyer la demande. Certaines opérations donnent lieu à plusieurs requêtes : batch.tables.add_columns() et batch.tables.remove_columns() correspondent chacun à une requête par colonne, de sorte que chaque colonne produit sa propre entrée dans la réponse.
Pour obtenir un exemple de lot complet, consultez examples/advanced/batch.py.
Informations connexes
- Personnaliser des tables et des colonnes
- Données de requête
- Gérer les erreurs et activer les diagnostics HTTP
- Exemples de code SDK pour Python
- SDK pour Python README