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.
- sqlobjects-1.0.11/CHANGELOG.md +97 -0
- sqlobjects-1.0.11/MANIFEST.in +4 -0
- {sqlobjects-1.0.9/sqlobjects.egg-info → sqlobjects-1.0.11}/PKG-INFO +21 -2
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/README.md +16 -1
- sqlobjects-1.0.11/docs/rules/01-database-session-guide.md +278 -0
- sqlobjects-1.0.11/docs/rules/02-model-definition-guide.md +389 -0
- sqlobjects-1.0.11/docs/rules/03-query-operations-guide.md +405 -0
- sqlobjects-1.0.11/docs/rules/04-crud-operations-guide.md +364 -0
- sqlobjects-1.0.11/docs/rules/05-relationships-guide.md +395 -0
- sqlobjects-1.0.11/docs/rules/06-validation-signals-guide.md +445 -0
- sqlobjects-1.0.11/docs/rules/07-performance-guide.md +488 -0
- sqlobjects-1.0.11/docs/rules/README.md +56 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/pyproject.toml +28 -10
- sqlobjects-1.0.11/setup.py +105 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/objects/core.py +25 -10
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/queries/builder.py +4 -3
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/queryset.py +76 -6
- {sqlobjects-1.0.9 → sqlobjects-1.0.11/sqlobjects.egg-info}/PKG-INFO +21 -2
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects.egg-info/SOURCES.txt +11 -0
- sqlobjects-1.0.11/sqlobjects.egg-info/requires.txt +9 -0
- sqlobjects-1.0.9/sqlobjects.egg-info/requires.txt +0 -1
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/LICENSE +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/setup.cfg +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/__init__.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/cascade.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/database/__init__.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/database/config.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/database/manager.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/exceptions.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/expressions/__init__.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/expressions/aggregate.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/expressions/base.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/expressions/function.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/expressions/mixins.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/expressions/scalar.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/expressions/subquery.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/expressions/terminal.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/__init__.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/core.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/functions.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/proxies.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/relations/__init__.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/relations/descriptors.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/relations/managers.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/relations/prefetch.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/relations/strategies.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/relations/utils.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/shortcuts.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/types/__init__.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/types/base.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/types/comparators.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/types/registry.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/fields/utils.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/internal/__init__.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/internal/operations.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/internal/results.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/metadata.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/mixins.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/model.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/objects/__init__.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/objects/bulk.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/objects/upsert.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/queries/__init__.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/queries/dialect.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/queries/executor.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/session.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/signals.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/utils/__init__.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/utils/inspect.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/utils/naming.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/utils/pattern.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects/validators.py +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects.egg-info/dependency_links.txt +0 -0
- {sqlobjects-1.0.9 → sqlobjects-1.0.11}/sqlobjects.egg-info/top_level.txt +0 -0
- {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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlobjects
|
|
3
|
-
Version: 1.0.
|
|
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
|
|
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
|
|
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
|
+
```
|