Skip to content

Commit 9e75dc5

Browse files
authored
Migration doc for Mgmt SDK that was generated from swagger but now are generated from typespec which is converted from swagger (#41045)
1 parent d5a168f commit 9e75dc5

File tree

1 file changed

+250
-0
lines changed

1 file changed

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

0 commit comments

Comments
 (0)