sqlobjects 1.0.9__tar.gz → 1.0.11__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. sqlobjects-1.0.11/CHANGELOG.md +97 -0
  2. sqlobjects-1.0.11/MANIFEST.in +4 -0
  3. {sqlobjects-1.0.9/sqlobjects.egg-info → sqlobjects-1.0.11}/PKG-INFO +21 -2
  4. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/README.md +16 -1
  5. sqlobjects-1.0.11/docs/rules/01-database-session-guide.md +278 -0
  6. sqlobjects-1.0.11/docs/rules/02-model-definition-guide.md +389 -0
  7. sqlobjects-1.0.11/docs/rules/03-query-operations-guide.md +405 -0
  8. sqlobjects-1.0.11/docs/rules/04-crud-operations-guide.md +364 -0
  9. sqlobjects-1.0.11/docs/rules/05-relationships-guide.md +395 -0
  10. sqlobjects-1.0.11/docs/rules/06-validation-signals-guide.md +445 -0
  11. sqlobjects-1.0.11/docs/rules/07-performance-guide.md +488 -0
  12. sqlobjects-1.0.11/docs/rules/README.md +56 -0
  13. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/pyproject.toml +28 -10
  14. sqlobjects-1.0.11/setup.py +105 -0
  15. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/objects/core.py +25 -10
  16. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/queries/builder.py +4 -3
  17. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/queryset.py +76 -6
  18. {sqlobjects-1.0.9 → sqlobjects-1.0.11/sqlobjects.egg-info}/PKG-INFO +21 -2
  19. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects.egg-info/SOURCES.txt +11 -0
  20. sqlobjects-1.0.11/sqlobjects.egg-info/requires.txt +9 -0
  21. sqlobjects-1.0.9/sqlobjects.egg-info/requires.txt +0 -1
  22. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/LICENSE +0 -0
  23. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/setup.cfg +0 -0
  24. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/__init__.py +0 -0
  25. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/cascade.py +0 -0
  26. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/database/__init__.py +0 -0
  27. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/database/config.py +0 -0
  28. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/database/manager.py +0 -0
  29. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/exceptions.py +0 -0
  30. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/expressions/__init__.py +0 -0
  31. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/expressions/aggregate.py +0 -0
  32. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/expressions/base.py +0 -0
  33. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/expressions/function.py +0 -0
  34. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/expressions/mixins.py +0 -0
  35. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/expressions/scalar.py +0 -0
  36. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/expressions/subquery.py +0 -0
  37. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/expressions/terminal.py +0 -0
  38. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/__init__.py +0 -0
  39. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/core.py +0 -0
  40. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/functions.py +0 -0
  41. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/proxies.py +0 -0
  42. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/relations/__init__.py +0 -0
  43. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/relations/descriptors.py +0 -0
  44. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/relations/managers.py +0 -0
  45. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/relations/prefetch.py +0 -0
  46. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/relations/strategies.py +0 -0
  47. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/relations/utils.py +0 -0
  48. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/shortcuts.py +0 -0
  49. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/types/__init__.py +0 -0
  50. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/types/base.py +0 -0
  51. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/types/comparators.py +0 -0
  52. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/types/registry.py +0 -0
  53. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/utils.py +0 -0
  54. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/internal/__init__.py +0 -0
  55. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/internal/operations.py +0 -0
  56. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/internal/results.py +0 -0
  57. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/metadata.py +0 -0
  58. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/mixins.py +0 -0
  59. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/model.py +0 -0
  60. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/objects/__init__.py +0 -0
  61. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/objects/bulk.py +0 -0
  62. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/objects/upsert.py +0 -0
  63. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/queries/__init__.py +0 -0
  64. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/queries/dialect.py +0 -0
  65. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/queries/executor.py +0 -0
  66. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/session.py +0 -0
  67. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/signals.py +0 -0
  68. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/utils/__init__.py +0 -0
  69. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/utils/inspect.py +0 -0
  70. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/utils/naming.py +0 -0
  71. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/utils/pattern.py +0 -0
  72. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/validators.py +0 -0
  73. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects.egg-info/dependency_links.txt +0 -0
  74. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects.egg-info/top_level.txt +0 -0
  75. {sqlobjects-1.0.9 → sqlobjects-1.0.11}/tests/test_config.py +0 -0
@@ -0,0 +1,97 @@
1
+ ## 1.0.11 (2025-11-18)
2
+
3
+ ### Feat
4
+
5
+ - add AI assistant rules with auto-install support
6
+
7
+ ## 1.0.10 (2025-11-17)
8
+
9
+ ### Feat
10
+
11
+ - support Model class in join methods for cleaner API
12
+
13
+ ## 1.0.9 (2025-11-14)
14
+
15
+ ### Feat
16
+
17
+ - add optional tables parameter to create_tables/drop_tables
18
+
19
+ ## 1.0.8 (2025-11-11)
20
+
21
+ ### Feat
22
+
23
+ - add model-level relationship loading methods
24
+ - improve relation field type inference
25
+ - refactor relationship proxies
26
+
27
+ ## 1.0.7 (2025-10-14)
28
+
29
+ ### Feat
30
+
31
+ - add upsert support for PostgreSQL
32
+
33
+ ### Fix
34
+
35
+ - identity support for PostgreSQL
36
+
37
+ ### Refactor
38
+
39
+ - consolidate bulk and queryset logic
40
+
41
+ ### Perf
42
+
43
+ - improve bulk delete performance
44
+
45
+ ## 1.0.6 (2025-10-08)
46
+
47
+ ### Feat
48
+
49
+ - optimize field cache and state manager
50
+ - implement relationship prefetch support
51
+ - add kwargs parameter support to filter/exclude/get methods
52
+
53
+ ### Refactor
54
+
55
+ - unify cascade for model and queryset operations
56
+
57
+ ## 1.0.5 (2025-09-25)
58
+
59
+ ### Fix
60
+
61
+ - generate DDL using column definition order
62
+
63
+ ## 1.0.4 (2025-09-25)
64
+
65
+ ### Feat
66
+
67
+ - remove unnecessary exception catching
68
+ - implement insert or update using database upsert
69
+
70
+ ### Fix
71
+
72
+ - use pk column name instead of column instance for pgsql upsert
73
+
74
+ ### Refactor
75
+
76
+ - move field default value related methods to DataConversionMixin
77
+
78
+ ## 1.0.3 (2025-09-25)
79
+
80
+ ### Fix
81
+
82
+ - field default/default_factory not working
83
+
84
+ ## 1.0.2 (2025-09-24)
85
+
86
+ ### Feat
87
+
88
+ - add type support for StringColumn
89
+ - add support for cascade delete in relationships
90
+ - add cascade support to relationship fields
91
+ - add type checking for __registry__
92
+ - use base model to create database tables
93
+ - init public commit
94
+
95
+ ### Fix
96
+
97
+ - foreign key type inference issue
@@ -0,0 +1,4 @@
1
+ include README.md
2
+ include LICENSE
3
+ include CHANGELOG.md
4
+ recursive-include docs/rules *.md
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlobjects
3
- Version: 1.0.9
3
+ Version: 1.0.11
4
4
  Summary: Django-style async ORM library based on SQLAlchemy with chainable queries, Q objects, and relationship loading
5
5
  Author-email: XtraVisions <gitadmin@xtravisions.com>, Chen Hao <chenhao@xtravisions.com>
6
6
  Maintainer-email: XtraVisions <gitadmin@xtravisions.com>, Chen Hao <chenhao@xtravisions.com>
@@ -26,6 +26,10 @@ Requires-Python: >=3.12
26
26
  Description-Content-Type: text/markdown
27
27
  License-File: LICENSE
28
28
  Requires-Dist: sqlalchemy[asyncio]>=2.0.43
29
+ Provides-Extra: amazonq
30
+ Provides-Extra: kiro
31
+ Provides-Extra: claude
32
+ Provides-Extra: cursor
29
33
  Dynamic: license-file
30
34
 
31
35
  # SQLObjects
@@ -216,6 +220,21 @@ SQLObjects is built on a solid foundation with clear architectural principles:
216
220
 
217
221
  ## 📖 Documentation
218
222
 
223
+ ### AI Assistant Rules (Quick Reference)
224
+
225
+ Best practices and usage patterns optimized for AI coding assistants:
226
+
227
+ - [AI Rules Overview](docs/rules/README.md) - Quick navigation and purpose
228
+ - [Database & Session Guide](docs/rules/01-database-session-guide.md) - Connection management and transactions
229
+ - [Model Definition Guide](docs/rules/02-model-definition-guide.md) - Model creation and field configuration
230
+ - [Query Operations Guide](docs/rules/03-query-operations-guide.md) - Filtering, sorting, and data retrieval
231
+ - [CRUD Operations Guide](docs/rules/04-crud-operations-guide.md) - Create, read, update, delete operations
232
+ - [Relationships Guide](docs/rules/05-relationships-guide.md) - Model relationships and loading strategies
233
+ - [Validation & Signals Guide](docs/rules/06-validation-signals-guide.md) - Data validation and lifecycle hooks
234
+ - [Performance Guide](docs/rules/07-performance-guide.md) - Optimization techniques and best practices
235
+
236
+ **Installation**: Rules are automatically installed with `pip install sqlobjects[amazonq]` (or `[cursor]`, `[claude]`, `[kiro]`)
237
+
219
238
  ### Feature Documentation
220
239
 
221
240
  - [Database Setup](docs/features/01-database-setup.md) - Database configuration and connection management
@@ -277,7 +296,7 @@ older_users = await User.objects.filter(User.age > avg_age).all()
277
296
 
278
297
  # Manual joins and locking
279
298
  posts = await Post.objects.join(
280
- User.__table__,
299
+ User, # Using Model class (recommended)
281
300
  Post.author_id == User.id
282
301
  ).select_for_update(nowait=True).all()
283
302
 
@@ -186,6 +186,21 @@ SQLObjects is built on a solid foundation with clear architectural principles:
186
186
 
187
187
  ## 📖 Documentation
188
188
 
189
+ ### AI Assistant Rules (Quick Reference)
190
+
191
+ Best practices and usage patterns optimized for AI coding assistants:
192
+
193
+ - [AI Rules Overview](docs/rules/README.md) - Quick navigation and purpose
194
+ - [Database & Session Guide](docs/rules/01-database-session-guide.md) - Connection management and transactions
195
+ - [Model Definition Guide](docs/rules/02-model-definition-guide.md) - Model creation and field configuration
196
+ - [Query Operations Guide](docs/rules/03-query-operations-guide.md) - Filtering, sorting, and data retrieval
197
+ - [CRUD Operations Guide](docs/rules/04-crud-operations-guide.md) - Create, read, update, delete operations
198
+ - [Relationships Guide](docs/rules/05-relationships-guide.md) - Model relationships and loading strategies
199
+ - [Validation & Signals Guide](docs/rules/06-validation-signals-guide.md) - Data validation and lifecycle hooks
200
+ - [Performance Guide](docs/rules/07-performance-guide.md) - Optimization techniques and best practices
201
+
202
+ **Installation**: Rules are automatically installed with `pip install sqlobjects[amazonq]` (or `[cursor]`, `[claude]`, `[kiro]`)
203
+
189
204
  ### Feature Documentation
190
205
 
191
206
  - [Database Setup](docs/features/01-database-setup.md) - Database configuration and connection management
@@ -247,7 +262,7 @@ older_users = await User.objects.filter(User.age > avg_age).all()
247
262
 
248
263
  # Manual joins and locking
249
264
  posts = await Post.objects.join(
250
- User.__table__,
265
+ User, # Using Model class (recommended)
251
266
  Post.author_id == User.id
252
267
  ).select_for_update(nowait=True).all()
253
268
 
@@ -0,0 +1,278 @@
1
+ # Database & Session Management Guide
2
+
3
+ ## Core Concepts
4
+
5
+ - **DatabaseManager**: Global singleton managing multiple database connections
6
+ - **Session**: Task-level database session with automatic transaction management
7
+ - **Context Managers**: `ctx_session()` and `ctx_sessions()` for transaction control
8
+ - **using() Pattern**: Bind operations to specific sessions or databases
9
+
10
+ ## Common Usage
11
+
12
+ ### Single Database Setup
13
+
14
+ ```python
15
+ from sqlobjects.database import init_db, create_tables
16
+ from sqlobjects.model import ObjectModel
17
+
18
+ # Initialize database
19
+ await init_db("sqlite+aiosqlite:///app.db")
20
+
21
+ # Create tables
22
+ await create_tables(ObjectModel)
23
+
24
+ # Use default session (automatic)
25
+ user = await User.objects.create(username="alice")
26
+ ```
27
+
28
+ ### Multi-Database Setup
29
+
30
+ ```python
31
+ from sqlobjects.database import init_dbs
32
+
33
+ # Configure multiple databases
34
+ await init_dbs({
35
+ "main": {"url": "postgresql+asyncpg://localhost/main", "pool_size": 20},
36
+ "analytics": {"url": "sqlite+aiosqlite:///analytics.db"}
37
+ }, default="main")
38
+
39
+ # Use specific database
40
+ user = await User.objects.using("analytics").create(username="analyst")
41
+ ```
42
+
43
+ ### Transaction Management
44
+
45
+ ```python
46
+ from sqlobjects.session import ctx_session, ctx_sessions
47
+
48
+ # Single database transaction
49
+ async with ctx_session() as session:
50
+ user = await User.objects.using(session).create(username="bob")
51
+ posts = await user.posts.using(session).all()
52
+ # Auto-commit on success, rollback on exception
53
+
54
+ # Multi-database transaction
55
+ async with ctx_sessions("main", "analytics") as sessions:
56
+ user = await User.objects.using(sessions["main"]).create(username="alice")
57
+ await Log.objects.using(sessions["analytics"]).create(message="User created")
58
+ ```
59
+
60
+ ### Session Binding
61
+
62
+ ```python
63
+ # Bind to session
64
+ async with ctx_session() as session:
65
+ # All operations use the same session
66
+ user = await User.objects.using(session).get(User.id == 1)
67
+ user.email = "new@example.com"
68
+ await user.using(session).save()
69
+
70
+ # Bind to named database
71
+ user = await User.objects.using("analytics").create(username="test")
72
+ ```
73
+
74
+ ## Best Practices
75
+
76
+ ### ✅ Do
77
+
78
+ - **Use context managers** for explicit transaction control
79
+ - **Bind operations to sessions** in complex transactions
80
+ - **Configure connection pools** based on your workload
81
+ - **Use read-only sessions** for query-only operations
82
+ - **Close databases** properly on application shutdown
83
+
84
+ ```python
85
+ # Good: Explicit transaction
86
+ async with ctx_session() as session:
87
+ user = await User.objects.using(session).create(username="alice")
88
+ await Post.objects.using(session).create(title="First Post", author_id=user.id)
89
+
90
+ # Good: Connection pool configuration
91
+ await init_db(
92
+ "postgresql://localhost/db",
93
+ pool_size=20,
94
+ max_overflow=30,
95
+ pool_timeout=30,
96
+ pool_recycle=3600
97
+ )
98
+ ```
99
+
100
+ ### ❌ Don't
101
+
102
+ - **Don't mix sessions** in related operations
103
+ - **Don't forget to close** database connections
104
+ - **Don't use tiny connection pools** in production
105
+ - **Don't create sessions manually** (use context managers)
106
+
107
+ ```python
108
+ # Bad: Mixing sessions
109
+ async with ctx_session() as session1:
110
+ user = await User.objects.using(session1).create(username="alice")
111
+ async with ctx_session() as session2:
112
+ # Different session - may cause issues
113
+ await Post.objects.using(session2).create(author_id=user.id)
114
+
115
+ # Bad: No transaction control
116
+ user = await User.objects.create(username="alice")
117
+ # If next operation fails, user is already created
118
+ await Post.objects.create(title="Post", author_id=user.id)
119
+ ```
120
+
121
+ ## Performance Tips
122
+
123
+ ### Connection Pooling
124
+
125
+ ```python
126
+ # Production configuration
127
+ await init_db(
128
+ "postgresql://localhost/db",
129
+ pool_size=20, # Base connections
130
+ max_overflow=30, # Burst capacity
131
+ pool_timeout=30, # Wait time for connection
132
+ pool_recycle=3600, # Recycle connections hourly
133
+ pool_pre_ping=True # Verify connections
134
+ )
135
+ ```
136
+
137
+ ### Session Reuse
138
+
139
+ ```python
140
+ # Reuse session across operations
141
+ async with ctx_session() as session:
142
+ # All operations share the same connection
143
+ users = await User.objects.using(session).all()
144
+ for user in users:
145
+ await user.posts.using(session).all()
146
+ ```
147
+
148
+ ### Read/Write Separation
149
+
150
+ ```python
151
+ # Use readonly parameter for read operations
152
+ from sqlobjects.session import get_session
153
+
154
+ # Read operations
155
+ session = get_session(readonly=True)
156
+ users = await User.objects.using(session).all()
157
+
158
+ # Write operations
159
+ session = get_session(readonly=False)
160
+ await User.objects.using(session).create(username="alice")
161
+ ```
162
+
163
+ ## Troubleshooting
164
+
165
+ ### Connection Pool Exhausted
166
+
167
+ **Problem**: `TimeoutError: QueuePool limit exceeded`
168
+
169
+ **Solution**:
170
+ ```python
171
+ # Increase pool size
172
+ await init_db(url, pool_size=30, max_overflow=50)
173
+
174
+ # Or use context managers to release connections
175
+ async with ctx_session() as session:
176
+ # Connection released after block
177
+ pass
178
+ ```
179
+
180
+ ### Transaction Deadlock
181
+
182
+ **Problem**: Operations hang or timeout
183
+
184
+ **Solution**:
185
+ ```python
186
+ # Use shorter transactions
187
+ async with ctx_session() as session:
188
+ # Keep transaction scope small
189
+ user = await User.objects.using(session).get(User.id == 1)
190
+ user.email = "new@example.com"
191
+ await user.using(session).save()
192
+ # Transaction commits here
193
+
194
+ # Don't hold transactions during I/O
195
+ async with ctx_session() as session:
196
+ user = await User.objects.using(session).get(User.id == 1)
197
+ # Transaction ends before external I/O
198
+ await send_email(user.email) # Outside transaction
199
+ ```
200
+
201
+ ### Session Not Found
202
+
203
+ **Problem**: `RuntimeError: No session available`
204
+
205
+ **Solution**:
206
+ ```python
207
+ # Always use context managers or explicit binding
208
+ async with ctx_session() as session:
209
+ user = await User.objects.using(session).create(username="alice")
210
+
211
+ # Or bind to database name
212
+ user = await User.objects.using("main").create(username="alice")
213
+ ```
214
+
215
+ ### Database Connection Lost
216
+
217
+ **Problem**: `OperationalError: connection closed`
218
+
219
+ **Solution**:
220
+ ```python
221
+ # Enable connection health checks
222
+ await init_db(url, pool_pre_ping=True, pool_recycle=3600)
223
+
224
+ # Handle connection errors gracefully
225
+ try:
226
+ user = await User.objects.get(User.id == 1)
227
+ except OperationalError:
228
+ # Reconnect or retry
229
+ await init_db(url)
230
+ user = await User.objects.get(User.id == 1)
231
+ ```
232
+
233
+ ## Complete Example
234
+
235
+ ```python
236
+ from sqlobjects.database import init_dbs, create_tables, close_all_dbs
237
+ from sqlobjects.session import ctx_session, ctx_sessions
238
+ from sqlobjects.model import ObjectModel
239
+ from sqlobjects.fields import Column, StringColumn
240
+
241
+ class User(ObjectModel):
242
+ username: Column[str] = StringColumn(length=50)
243
+ email: Column[str] = StringColumn(length=100)
244
+
245
+ class Log(ObjectModel):
246
+ message: Column[str] = StringColumn(length=500)
247
+
248
+ async def main():
249
+ # Setup
250
+ await init_dbs({
251
+ "main": {"url": "postgresql://localhost/main", "pool_size": 20},
252
+ "analytics": {"url": "sqlite:///analytics.db"}
253
+ }, default="main")
254
+
255
+ await create_tables(ObjectModel, "main")
256
+ await create_tables(ObjectModel, "analytics")
257
+
258
+ # Single database transaction
259
+ async with ctx_session() as session:
260
+ user = await User.objects.using(session).create(
261
+ username="alice",
262
+ email="alice@example.com"
263
+ )
264
+
265
+ # Multi-database transaction
266
+ async with ctx_sessions("main", "analytics") as sessions:
267
+ user = await User.objects.using(sessions["main"]).get(User.username == "alice")
268
+ await Log.objects.using(sessions["analytics"]).create(
269
+ message=f"User {user.username} logged in"
270
+ )
271
+
272
+ # Cleanup
273
+ await close_all_dbs()
274
+
275
+ # Run
276
+ import asyncio
277
+ asyncio.run(main())
278
+ ```