microsoft / microsoft/PowerPlatform-DataverseClient-Python
tables.create / add_columns cannot set column constraints (MaxLength, MinValue/MaxValue, Format, RequiredLevel, DisplayName)
@vrathee-msft is already working on this.
Since Aug 17, 2026.
- Dominant language
- Python
- Stars
- 60
- Forks
- 23
- Avg merge
- 13h 53m
- Merged PRs (30d)
- 3
Description
Summary
client.tables.create(...) and client.tables.add_columns(...) accept a column spec of {schema_name: type} where the value is only a type string (or an Enum subclass for optionsets). There is no way to set per-column constraints -- MaxLength, MinValue/MaxValue, Format/FormatName, Precision, RequiredLevel, or a custom DisplayName. Every constraint is hardcoded per type.
Evidence (main @ 5601657)
src/PowerPlatform/Dataverse/data/_odata_base.py::_attribute_payload:
- Rejects any non-
str/ non-Enumspec:raise ValueError("... expected str or Enum subclass"). - Hardcodes constraints per type:
string->MaxLength: 200;memo->MaxLength: 4000;int->MinValue: -2147483648, MaxValue: 2147483647;decimal/double-> fixed Min/Max +Precision;RequiredLevel: {"Value": "None"}on all branches;DisplayNamederived from the schema-name suffix.
No public create_attribute / raw-metadata / generic-request method exists in the tables API (only create, add_columns, remove_columns, create_alternate_key), so there is no managed escape hatch either.
Impact
To create, for example, a Rating column limited to 1-5 or a Comment column with MaxLength=2000, callers must drop to the raw Web API EntityDefinitions({id})/Attributes endpoint and hand-build the payload -- including the @odata.type derived-type discriminator and using PUT (not PATCH) for updates. On hosts where the Dataverse CLI cannot run (ChatGPT web / Codex sandbox -- no .NET runtime), the SDK is the primary path, so this gap forces error-prone hand-rolled metadata calls for a very common ask ("create a table with a bounded rating and a sized text field").
Proposed change (backward-compatible)
Extend the column-spec value to also accept a dict, keeping str / Enum working exactly as today:
client.tables.create("cfb_CustomerFeedback", {
"cfb_Name": "string", # unchanged
"cfb_Rating": {"type": "int", "min_value": 1, "max_value": 5},
"cfb_Comment": {"type": "memo", "max_length": 2000, "display_name": "Comment"},
})
_attribute_payload builds the base payload for type, then applies the provided overrides:
max_length->MaxLengthmin_value/max_value->MinValue/MaxValueprecision->Precisionformat->Format(int/date) orFormatName.Value(string/memo)required->RequiredLevel.Value(None/ApplicationRequired/Recommended)display_name->DisplayNamelabel
The @odata.type discriminator is already stamped correctly, so this needs no endpoint changes.
Touchpoints
data/_odata_base.py::_attribute_payload(core -- shared by the sync + async paths).- Docstrings / typing for
operations/tables.py+aio/operations/async_tables.py(create,add_columns) andoperations/batch.py. - Unit tests mirroring the existing
_attribute_payloadtype tests (add override cases). - README + CHANGELOG per the repo maintenance rules.
Acceptance
{"type": "memo", "max_length": 2000}produces a MemoAttributeMetadata payload withMaxLength: 2000.{"type": "int", "min_value": 1, "max_value": 5}produces an IntegerAttributeMetadata payload with those bounds.strandEnumspecs behave exactly as before (no breaking change).- Unit tests cover the overrides and the unchanged str/Enum paths.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.