1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
|
# Azure SDK Migration Guide: New Hybrid Model Design Generation Breaking Changes
The direct link to this page can be found at aka.ms/azsdk/python/migrate/hybrid-models
This guide covers the breaking changes you'll encounter when upgrading to our new model design and how to fix them in your code.
Our new hybrid models are named as such because they have a dual dictionary and model nature.
## Summary of Breaking Changes
When migrating to the hybrid model design, expect these breaking changes:
| Change | Impact | Quick Fix |
| ----------------------------------------------------------------------------------- | --------------------------------------------------------- | --------------------------------------------------------------------------------- |
| [Dictionary Access](#dictionary-access-syntax) | `as_dict()` parameter renamed, output format changed | Recommended removal of `as_dict()` and directly access model, or replace `keep_readonly=True` with `exclude_readonly=False`, expect `camelCase` keys |
| [Model Hierarchy](#model-hierarchy-reflects-rest-api-structure) | Multi-level flattened properties removed | Replace `obj.level1_level2_prop` with `obj.level1.level2.prop` |
| [Additional Properties](#additional-properties-handling) | `additional_properties` parameter removed | Use direct dictionary syntax: `model["key"] = value` |
| [String Representation](#string-representation-matches-rest-api) | Model key output changed from `snake_case` to `camelCase` | Update any code parsing model strings to expect `camelCase` |
| [Serialization/Deserialization](#serialization-and-deserialization-methods-removed) | `serialization` and `deserialization` methods removed | Use dictionary access for serialization, constructor for deserialization |
## Detailed Breaking Changes
### Dictionary Access Syntax
**What changed**: Hybrid models support direct dictionary access and use different parameter names and output formats compared to our old models.
**What will break**:
- Code that relies on parameter `keep_readonly` to `.on_dict()`
- Code that expects `snake_case` keys in dictionary output
**Before**:
```python
from azure.mgmt.test.models import Model
model = Model(name="example")
# Dictionary access required as_dict()
json_model = model.as_dict(keep_readonly=True)
print(json_model["my_name"]) # snake_case key
```
**After**:
```python
from azure.mgmt.test.models import Model
model = Model(name="example")
# Direct dictionary access now works
print(model["myName"]) # Works directly
# as_dict() parameter changed
json_model = model.as_dict(exclude_readonly=False) # Parameter renamed
print(json_model["myName"]) # Now returns camelCase key (matches REST API)
```
**Migration steps:**
- (Recommended) If you don't need a memory copy as a dict, simplify code by using direct dictionary access: `model["key"]` instead of `model.as_dict()["key"]`
- Replace `keep_readonly=True` with `exclude_readonly=False`
- Update code expecting `snake_case` keys to use `camelCase` keys (consistent with REST API)
### Model Hierarchy Reflects REST API Structure
**What changed**: Hybrid model generation preserves the actual REST API hierarchy instead of artificially flattening it.
**What will break**:
- We've maintained backcompat for attribute access for single-level flattened properties, but multi-level flattening will no longer be supported.
- No level of flattening will be supported when dealing with the the response object from `.to_dict()`.
**Before**:
```python
model = Model(...)
print(model.properties_name) # Works
print(model.properties_properties_name) # Works (artificially flattened)
json_model = model.as_dict()
print(json_model["properties_properties_name"]) # Works (artificially flattened)
```
**After**:
```python
model = Model(...)
print(model.properties_name) # Still works (single-level flattening maintained for compatibility)
print(model.properties.name) # Equivalent to above, preferred approach
print(model["properties_name"]) # ❌ Raises KeyError
print(model.properties_properties_name) # ❌ Raises AttributeError
print(model.properties.properties.name) # ✅ Mirrors actual API structure
print(model["properties_properties_name"]) # ❌ Raises KeyError
print(model["properties"]["properties"]["name"]) # ✅ Mirrors actual API structure
```
**Migration steps:**
Identify any properties with multiple underscores that represent nested structures
Replace them with the actual nested property access using dot notation
Example: `obj.level1_level2_property` → `obj.level1.level2.property`
This new structure will match your REST API documentation exactly
### Additional Properties Handling
**What changed**: Hybrid models inherently support additional properties through dictionary-like behavior, eliminating the need for a separate additional_properties parameter.
**What will break**:
- Code that passes `additional_properties` parameter
- Code that reads `.additional_properties` attribute
**Before**:
```python
# Setting additional properties
model = Model(additional_properties={"custom": "value"})
print(model.additional_properties) # {"custom": "value"}
```
**After**:
```python
# ❌ Raises TypeError
model = Model(additional_properties={"custom": "value"})
# ✅ Use these approaches instead
model = Model({"custom": "value"})
# OR
model = Model()
model.update({"custom": "value"})
# OR
model = Model()
model["custom"] = "value"
print(model) # Shows the additional properties directly
```
**Migration steps:**
- Remove all `additional_properties=` parameters from model constructors
- Replace with direct dictionary syntax or `.update()` calls
- Replace `.additional_properties` attribute access with direct dictionary access
### String Representation Matches REST API
**What changed**: Hybrid models string output uses `camelCase` (matching the REST API) instead of Python's `snake_case` convention in our old models.
**What will break**:
- Code that parses or matches against model string representations
- Tests that compare string output
**Before**:
```python
model = Model(type_name="example")
print(model) # {"type_name": "example"}
```
**After**:
```python
model = Model(type_name="example")
print(model) # {"typeName": "example"} - matches REST API format
```
**Migration steps:**
- Update any code that parses model string representations to expect `camelCase`
- Update test assertions that compare against model string output
- Consider using property access instead of string parsing where possible
### Serialization and Deserialization Methods Removed
**What changed**: Hybrid models no longer include explicit `serialize()` and `deserialize()` methods. Models are now inherently serializable through dictionary access, and deserialization happens automatically through the constructor.
**What will break**:
- Code that calls `model.serialize()` or `Model.deserialize()`
- Custom serialization/deserialization workflows
- Code that depends on the specific format returned by the old serialization methods
**Before**:
```python
from azure.mgmt.test.models import Model
import json
# Serialization
model = Model(name="example", value=42)
serialized_dict = model.serialize() # Returns dict using the REST API name, compatible with `json.dump`
json_string = json.dumps(serialized_dict)
# Deserialization
json_data = json.loads(json_string)
model = Model.deserialize(json_data) # Static method for deserialization
print(model.name) # "example"
# Custom serialization with options
serialized_full = model.serialize(keep_readonly=True)
serialized_minimal = model.serialize(keep_readonly=False)
```
**After**:
```python
from azure.mgmt.test.models import Model
import json
# Serialization - model is already in serialized format when accessed as dictionary
model = Model(name="example", value=42)
# Method 1: Explicit as_dict() method (recommended)
json_string = json.dumps(model.as_dict())
# Method 2: Direct dictionary access
serialized_dict = {}
for key in model:
serialized_dict[key] = model[key]
# Deserialization - pass JSON dict directly to constructor
json_data = json.loads(json_string)
model = Model(json_data) # Constructor handles deserialization automatically
print(model.name) # "example"
# Advanced: Constructor also accepts keyword arguments
model = Model(name="example", value=42) # Still works as before
```
**Migration steps:**
- Replace serialization calls: `model.serialize()` → `model` or `model.as_dict()` or `dict(model)`
- Replace deserialization calls: `Model.deserialize(data) → Model(data)`
- Remove any static method imports
- Update serialization options:
- `serialize(keep_readonly=True)` → `as_dict(exclude_readonly=False)`
- `serialize(keep_readonly=False)` → `as_dict(exclude_readonly=True)`
- Test serialization format:
- Verify the output format matches your expectations
- Check that `camelCase` keys are handled correctly
## Why These Changes?
Our hybrid models prioritize consistency with the underlying REST API:
- **Better API Alignment**: Model hierarchy and property names now match your REST API documentation exactly
- **Improved Developer Experience**: Direct dictionary access eliminates extra method calls
- **Consistency**: `camelCase` output matches what you see in REST API responses
- **Maintainability**: Reduced artificial flattening makes the SDK easier to maintain and understand
If you encounter issues not covered here, please file an issue on [GitHub](https://github.com/microsoft/typespec/issues) with tag `emitter:client:python`.
|