Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Este artigo demonstra o código de exemplo que usa o SDK para trabalhar com dados e metadados do Dataverse. Antes de continuar, leia Introdução.
Operações básicas
Aqui está um código de exemplo que opera na tabela da conta.
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)
Gerenciador de contexto
O Gerenciador de Contexto manipula a limpeza automática e o pool de conexões HTTP. Use a sintaxe a seguir para aproveitar o Gerenciador de Contexto.
with DataverseClient("https://<myorg>.crm.dynamics.com", credential) as client:
O código de trabalho a seguir demonstra como usar o Gerenciador de Contexto.
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
Operações em massa
Aqui estão alguns exemplos que executam operações em massa.
# 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)
O exemplo a seguir cria várias contas. Passe uma lista de cargas úteis para create(logical_name, payloads) a fim de invocar a ação associada à coleção Microsoft.Dynamics.CRM.CreateMultiple. O método retorna list[str] dos IDs de registros criados.
# 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})
Para obter mais informações sobre operações em massa:
-
updateretornaNonetanto para chamadas de um único registro quanto para chamadas em lote, para manter a semântica consistente. - Transmissão versus por registro é determinado pela possibilidade do parâmetro
changesser um dicionário ou uma lista. - O atributo de chave primária é injetado automaticamente ao construir os destinos da ação UpdateMultiple.
- Se algum conteúdo omitir @odata.type, o SDK o carimba automaticamente (pesquisa de nome lógico em cache).
- A resposta inclui apenas IDs – o SDK retorna essas cadeias de caracteres GUID.
-
createretorna o GUID do novo registro como umstrquando você passa um único dicionário e umlist[str]quando você passa uma lista de cargas. - A pesquisa de metadados para
@odata.typeé realizada uma vez por conjunto de entidades (em cache na memória).
Executar upsert (criar e atualizar)
Uma sequência de acesso a dados comum é primeiro verificar se existe uma linha de tabela. Se a linha existir, atualize-a. Caso contrário, crie a linha. Você pode tornar essa sequência mais eficiente usando uma única chamada de API da operação Upsert.
Para obter mais informações, consulte Usar Upsert para criar ou atualizar um registro.
Importante
A tabela deve ter uma chave alternativa configurada no Dataverse para as colunas usadas em alternate_key. Defina chaves alternativas nos metadados da tabela por meio do portal do criador de Power Apps ou de uma chamada à API do Dataverse. Sem uma chave alternativa configurada, o Dataverse rejeita solicitações upsert com um erro 400.
Use client.records.upsert() para criar ou atualizar registros identificados por chaves alternativas. Quando a chave corresponde a um registro existente, o método atualiza o registro. Caso contrário, ele criará o registro. Um item único usa uma solicitação PATCH, enquanto vários itens usam a ação em massa UpsertMultiple.
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"},
}
])
Estruturas de Dados (DataFrames)
O SDK fornece wrappers de pandas para todas as operações CRUD por meio do namespace client.dataframe. Esses wrappers usam as APIs de DataFrame e Series do pandas para entrada e saída.
Note
client.dataframe.get() está obsoleto. Use os padrões de GA mostrados nas seções a seguir.
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"
)
Carregar arquivos no Dataverse
O exemplo a seguir mostra como carregar um arquivo nomeado document.pdf para a coluna Arquivo nomeada new_Document de um registro de conta. O SDK para Python manipula automaticamente o agrupamento de arquivos para arquivos com mais de 128 MB.
# Upload a file to a record
client.files.upload(
"account",
account_id,
"new_Document",
"/path/to/document.pdf",
)
Importante
Se a coluna de arquivo não existir, o SDK a criará automaticamente. Criar uma coluna é uma alteração de metadados: você precisa de privilégios para personalizar a tabela e a nova coluna se torna uma parte permanente do esquema do seu ambiente. Em ambientes de produção, crie colunas de arquivo com antecedência em vez de depender da criação automática.
O upload método aceita parâmetros opcionais para controlar o tipo de conteúdo e a estratégia de transferência.
# 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,
)
O mode parâmetro controla como o SDK transfere o arquivo.
| Mode | Behavior |
|---|---|
"auto" (predefinição) |
"small" Seleciona arquivos com menos de 128 MB ou "chunk" arquivos de 128 MB e maiores. |
"small" |
Envia todo o arquivo em uma única solicitação. Gera um ValueError se o arquivo exceder 128 MB. |
"chunk" |
Transmite o arquivo em segmentos. Use para arquivos grandes ou para forçar a transferência em partes, independentemente do tamanho. |
Note
Restrições de tamanho de arquivo do Dataverse se aplicam a uploads de arquivo (128 MB por arquivo por padrão). Carregar um arquivo maior com mode="small" gera um ValueError. Use mode="chunk" ou o padrão mode="auto". O cliente assíncrono expõe o mesmo files.upload método. Chame-o com await.
Operações em lote
Use client.batch para enviar várias operações em uma solicitação HTTP. O namespace de lote espelha client.records, client.tables e 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}")
Conjunto de alterações transacionais
Todas as operações em um conjunto de alterações são concluídas com êxito ou revertidas em conjunto.
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")
Dentro de um conjunto de alterações, cs.records.create() retorna uma cadeia de caracteres de referência de ID de conteúdo, como "$1". Use-o no mesmo conjunto de alterações, sem aguardar uma resposta do servidor, seja como o valor de um campo @odata.bind (conforme mostrado anteriormente) ou como o argumento record_id para uma chamada posterior a cs.records.delete() ou cs.records.update(). Operações de leitura (retrieve, list) não são permitidas dentro de um conjunto de alterações.
Metadados de tabela e consultas SQL em um lote
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()
Continuar em caso de erro
Tente todas as operações mesmo se uma falhar.
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}")
Integração do DataFrame
Alimente DataFrames de pandas diretamente em um lote.
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()
Gerenciar respostas de lote e limites
batch.execute() retorna um BatchResult. Sua lista responses contém uma entrada por operação HTTP, na ordem de submissão. As listas succeeded e failed são subconjuntos de responses. A propriedade has_errors é True quando alguma operação falhar. A lista entity_ids coleta os GUIDs do cabeçalho OData-EntityId de operações bem-sucedidas de criação e atualização de registro único.
Cada entrada de resposta expõe status_code, is_success, entity_id, data (corpo processado para operações de recuperação, listagem e consulta), error_message e error_code. Diferentes operações retornam formas diferentes:
-
Criação de um único registro — status 204,
entity_idpreenchido. -
Criar em massa ou fazer upsert — status 200, IDs em
item.data["Ids"](sem cabeçalhoOData-EntityId). -
Consulta ou lista SQL — status 200, linhas em
item.data["value"]. - Excluir ou gravar metadados — status 204, sem dados do corpo.
O número máximo de operações HTTP por lote é de 1.000. Se você exceder esse limite, o cliente gerará um ValidationError antes de enviar a solicitação. Algumas operações se expandem para mais de uma solicitação: batch.tables.add_columns() e batch.tables.remove_columns() cada uma resolve para uma solicitação por coluna, de modo que cada coluna produz sua própria entrada de resposta.
Para obter um exemplo completo em lote, consulte examples/advanced/batch.py.
Informações relacionadas
- Personalizar tabelas e colunas
- Consultar dados
- Manipular erros e habilitar o diagnóstico HTTP
- Exemplos de código do SDK para Python
- SDK para Python README