Skip to content

Commit 5280c46

Browse files
author
Saurabh Badenkal
committed
Expose primary_name_attribute in TableInfo, fix prodev example E2E
SDK changes: - _get_entity_by_table_schema_name now selects PrimaryNameAttribute and PrimaryIdAttribute from EntityDefinitions - _create_table returns primary_name_attribute in its result dict - TableInfo model includes primary_name_attribute and primary_id_attribute fields with legacy key access - Fixes #148: tables.create() now exposes the primary column logical name Example changes: - prodev_quick_start.py uses result.primary_name_attribute from tables.create() to construct correct record payloads (fixes the demo_*_name column error) - Both examples verified E2E against live Dataverse
1 parent 860d10b commit 5280c46

4 files changed

Lines changed: 40 additions & 19 deletions

File tree

examples/advanced/prodev_quick_start.py

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -100,19 +100,19 @@ def run_demo(client):
100100
print(f"[INFO] Output folder: {OUTPUT_DIR.resolve()}")
101101

102102
# -- Step 1: Create 4 tables --
103-
step1_create_tables(client)
103+
primary_name_col = step1_create_tables(client)
104104

105105
# -- Step 2: Create relationships --
106106
step2_create_relationships(client)
107107

108108
# -- Step 3: Populate with sample data --
109-
customer_ids, project_ids, task_ids = step3_populate_data(client)
109+
customer_ids, project_ids, task_ids = step3_populate_data(client, primary_name_col)
110110

111111
# -- Step 4: Query and analyze --
112-
step4_query_and_analyze(client, customer_ids)
112+
step4_query_and_analyze(client, customer_ids, primary_name_col)
113113

114114
# -- Step 5: Update and delete --
115-
step5_update_and_delete(client, task_ids)
115+
step5_update_and_delete(client, task_ids, primary_name_col)
116116

117117
# -- Step 6: Cleanup --
118118
cleanup(client)
@@ -134,15 +134,18 @@ def step1_create_tables(client):
134134
print("-" * 60)
135135

136136
# Customer table
137-
client.tables.create(
137+
result = client.tables.create(
138138
TABLE_CUSTOMER,
139139
{
140140
f"{TABLE_CUSTOMER}_Email": "string",
141141
f"{TABLE_CUSTOMER}_Industry": "string",
142142
f"{TABLE_CUSTOMER}_Revenue": "money",
143143
},
144144
)
145-
print(f"[OK] Created table: {TABLE_CUSTOMER}")
145+
# The primary name column logical name is returned by tables.create()
146+
# so we know exactly what key to use in create payloads.
147+
primary_name_col = result.primary_name_attribute
148+
print(f"[OK] Created table: {TABLE_CUSTOMER} (primary column: {primary_name_col})")
146149

147150
# Project table
148151
client.tables.create(
@@ -177,6 +180,9 @@ def step1_create_tables(client):
177180
)
178181
print(f"[OK] Created table: {TABLE_TIMEENTRY}")
179182
print(f"[OK] All 4 tables created with prefix '{PREFIX}'")
183+
print(f"[INFO] Primary name column: '{primary_name_col}'")
184+
185+
return primary_name_col
180186

181187

182188
# ================================================================
@@ -225,7 +231,7 @@ def step2_create_relationships(client):
225231
# ================================================================
226232

227233

228-
def step3_populate_data(client):
234+
def step3_populate_data(client, primary_name_col):
229235
"""Create sample records using client.dataframe.create().
230236
231237
Why DataFrames here instead of client.records.create()?
@@ -252,10 +258,8 @@ def step3_populate_data(client):
252258
print("-" * 60)
253259

254260
# -- Customers --
255-
# Note: The primary column logical name is {prefix}_name (lowercase),
256-
# and custom column logical names follow the same lowercase convention.
257-
# The SDK lowercases keys automatically, so we use the schema names here.
258-
name_col = f"{PREFIX}_Name"
261+
# Use the primary name column returned by tables.create()
262+
name_col = primary_name_col
259263
customers_df = pd.DataFrame(
260264
[
261265
{
@@ -364,15 +368,15 @@ def step3_populate_data(client):
364368
# ================================================================
365369

366370

367-
def step4_query_and_analyze(client, customer_ids):
371+
def step4_query_and_analyze(client, customer_ids, primary_name_col):
368372
"""Query data and demonstrate DataFrame analysis."""
369373
print("\n" + "-" * 60)
370374
print("STEP 4: Query and analyze data")
371375
print("-" * 60)
372376

373377
# Query all projects as a DataFrame
374378
# Note: select uses logical names (lowercase). The SDK lowercases automatically.
375-
name_attr = f"{PREFIX}_name" # primary column logical name
379+
name_attr = primary_name_col
376380
projects = client.dataframe.get(
377381
TABLE_PROJECT,
378382
select=[
@@ -437,7 +441,7 @@ def step4_query_and_analyze(client, customer_ids):
437441
# ================================================================
438442

439443

440-
def step5_update_and_delete(client, task_ids):
444+
def step5_update_and_delete(client, task_ids, primary_name_col):
441445
"""Demonstrate update and delete with DataFrames."""
442446
print("\n" + "-" * 60)
443447
print("STEP 5: Update and delete records")
@@ -461,10 +465,9 @@ def step5_update_and_delete(client, task_ids):
461465
print(f"[OK] Deleted 1 task")
462466

463467
# Verify
464-
name_attr = f"{PREFIX}_name"
465468
remaining = client.dataframe.get(
466469
TABLE_TASK,
467-
select=[name_attr, status_col],
470+
select=[primary_name_col, status_col],
468471
)
469472
print(f"\n Remaining tasks ({len(remaining)}):")
470473
print(f"{remaining.to_string(index=False)}")

src/PowerPlatform/Dataverse/data/_odata.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -960,7 +960,7 @@ def _get_entity_by_table_schema_name(
960960
logical_lower = table_schema_name.lower()
961961
logical_escaped = self._escape_odata_quotes(logical_lower)
962962
params = {
963-
"$select": "MetadataId,LogicalName,SchemaName,EntitySetName",
963+
"$select": "MetadataId,LogicalName,SchemaName,EntitySetName,PrimaryNameAttribute,PrimaryIdAttribute",
964964
"$filter": f"LogicalName eq '{logical_escaped}'",
965965
}
966966
r = self._request("get", url, params=params, headers=headers)
@@ -1689,6 +1689,8 @@ def _create_table(
16891689
"table_logical_name": metadata.get("LogicalName"),
16901690
"entity_set_name": metadata.get("EntitySetName"),
16911691
"metadata_id": metadata.get("MetadataId"),
1692+
"primary_name_attribute": metadata.get("PrimaryNameAttribute"),
1693+
"primary_id_attribute": metadata.get("PrimaryIdAttribute"),
16921694
"columns_created": created_cols,
16931695
}
16941696

src/PowerPlatform/Dataverse/models/table_info.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ class TableInfo:
112112
logical_name: str = ""
113113
entity_set_name: str = ""
114114
metadata_id: str = ""
115+
primary_name_attribute: Optional[str] = None
116+
primary_id_attribute: Optional[str] = None
115117
display_name: Optional[str] = None
116118
description: Optional[str] = None
117119
columns: Optional[List[ColumnInfo]] = field(default=None, repr=False)
@@ -123,6 +125,8 @@ class TableInfo:
123125
"table_logical_name": "logical_name",
124126
"entity_set_name": "entity_set_name",
125127
"metadata_id": "metadata_id",
128+
"primary_name_attribute": "primary_name_attribute",
129+
"primary_id_attribute": "primary_id_attribute",
126130
"columns_created": "columns_created",
127131
}
128132

@@ -187,6 +191,8 @@ def from_dict(cls, data: Dict[str, Any]) -> TableInfo:
187191
logical_name=data.get("table_logical_name", ""),
188192
entity_set_name=data.get("entity_set_name", ""),
189193
metadata_id=data.get("metadata_id", ""),
194+
primary_name_attribute=data.get("primary_name_attribute"),
195+
primary_id_attribute=data.get("primary_id_attribute"),
190196
columns_created=data.get("columns_created"),
191197
)
192198

@@ -213,6 +219,8 @@ def from_api_response(cls, response_data: Dict[str, Any]) -> TableInfo:
213219
logical_name=response_data.get("LogicalName", ""),
214220
entity_set_name=response_data.get("EntitySetName", ""),
215221
metadata_id=response_data.get("MetadataId", ""),
222+
primary_name_attribute=response_data.get("PrimaryNameAttribute"),
223+
primary_id_attribute=response_data.get("PrimaryIdAttribute"),
216224
display_name=display_name,
217225
description=description,
218226
)

tests/unit/models/test_table_info.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,19 @@ def test_legacy_key_iteration(self):
4949
keys = list(self.info)
5050
self.assertEqual(
5151
keys,
52-
["table_schema_name", "table_logical_name", "entity_set_name", "metadata_id", "columns_created"],
52+
[
53+
"table_schema_name",
54+
"table_logical_name",
55+
"entity_set_name",
56+
"metadata_id",
57+
"primary_name_attribute",
58+
"primary_id_attribute",
59+
"columns_created",
60+
],
5361
)
5462

5563
def test_len(self):
56-
self.assertEqual(len(self.info), 5)
64+
self.assertEqual(len(self.info), 7)
5765

5866
def test_keys_values_items(self):
5967
self.assertEqual(list(self.info.keys()), list(self.info._LEGACY_KEY_MAP.keys()))

0 commit comments

Comments
 (0)