vention-storage 0.5.1__tar.gz → 0.6.5__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.
- vention_storage-0.6.5/PKG-INFO +494 -0
- vention_storage-0.6.5/README.md +462 -0
- vention_storage-0.6.5/pyproject.toml +120 -0
- {vention_storage-0.5.1 → vention_storage-0.6.5}/src/storage/accessor.py +177 -55
- vention_storage-0.6.5/src/storage/bootstrap.py +34 -0
- vention_storage-0.6.5/src/storage/crud_service.py +362 -0
- {vention_storage-0.5.1 → vention_storage-0.6.5}/src/storage/database.py +3 -9
- vention_storage-0.6.5/src/storage/database_service.py +235 -0
- {vention_storage-0.5.1 → vention_storage-0.6.5}/src/storage/hooks.py +3 -9
- {vention_storage-0.5.1 → vention_storage-0.6.5}/src/storage/io_helpers.py +15 -15
- vention_storage-0.6.5/src/storage/utils.py +43 -0
- vention_storage-0.6.5/src/storage/vention_communication.py +537 -0
- vention_storage-0.5.1/PKG-INFO +0 -509
- vention_storage-0.5.1/README.md +0 -491
- vention_storage-0.5.1/pyproject.toml +0 -34
- vention_storage-0.5.1/src/storage/bootstrap.py +0 -47
- vention_storage-0.5.1/src/storage/router_database.py +0 -275
- vention_storage-0.5.1/src/storage/router_model.py +0 -247
- vention_storage-0.5.1/src/storage/utils.py +0 -20
- {vention_storage-0.5.1 → vention_storage-0.6.5}/src/storage/__init__.py +0 -0
- {vention_storage-0.5.1 → vention_storage-0.6.5}/src/storage/auditor.py +0 -0
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vention-storage
|
|
3
|
+
Version: 0.6.5
|
|
4
|
+
Summary: A framework for storing and managing component and application data for machine apps.
|
|
5
|
+
License: Proprietary
|
|
6
|
+
Author: VentionCo
|
|
7
|
+
Requires-Python: >=3.10,<3.11
|
|
8
|
+
Classifier: License :: Other/Proprietary License
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Requires-Dist: annotated-doc (==0.0.4) ; python_version == "3.10"
|
|
12
|
+
Requires-Dist: annotated-types (==0.7.0) ; python_version == "3.10"
|
|
13
|
+
Requires-Dist: anyio (==4.12.1) ; python_version == "3.10"
|
|
14
|
+
Requires-Dist: click (==8.3.1) ; python_version == "3.10"
|
|
15
|
+
Requires-Dist: colorama (==0.4.6) ; python_version == "3.10" and platform_system == "Windows"
|
|
16
|
+
Requires-Dist: exceptiongroup (==1.3.1) ; python_version == "3.10"
|
|
17
|
+
Requires-Dist: fastapi (==0.121.1) ; python_version == "3.10"
|
|
18
|
+
Requires-Dist: greenlet (==3.3.1) ; python_version == "3.10" and (platform_machine == "aarch64" or platform_machine == "ppc64le" or platform_machine == "x86_64" or platform_machine == "amd64" or platform_machine == "AMD64" or platform_machine == "win32" or platform_machine == "WIN32")
|
|
19
|
+
Requires-Dist: h11 (==0.16.0) ; python_version == "3.10"
|
|
20
|
+
Requires-Dist: idna (==3.11) ; python_version == "3.10"
|
|
21
|
+
Requires-Dist: pydantic (==2.12.5) ; python_version == "3.10"
|
|
22
|
+
Requires-Dist: pydantic-core (==2.41.5) ; python_version == "3.10"
|
|
23
|
+
Requires-Dist: python-multipart (==0.0.20) ; python_version == "3.10"
|
|
24
|
+
Requires-Dist: sqlalchemy (==2.0.46) ; python_version == "3.10"
|
|
25
|
+
Requires-Dist: sqlmodel (==0.0.27) ; python_version == "3.10"
|
|
26
|
+
Requires-Dist: starlette (==0.49.3) ; python_version == "3.10"
|
|
27
|
+
Requires-Dist: typing-extensions (==4.15.0) ; python_version == "3.10"
|
|
28
|
+
Requires-Dist: typing-inspection (==0.4.2) ; python_version == "3.10"
|
|
29
|
+
Requires-Dist: uvicorn (==0.35.0) ; python_version == "3.10"
|
|
30
|
+
Requires-Dist: vention-communication (==0.3.0) ; python_version == "3.10"
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# Vention Storage
|
|
34
|
+
|
|
35
|
+
A framework for storing and managing component and application data with persistence, validation, and audit trails for machine applications.
|
|
36
|
+
|
|
37
|
+
## Table of Contents
|
|
38
|
+
|
|
39
|
+
- [✨ Features](#-features)
|
|
40
|
+
- [🧠 Concepts & Overview](#-concepts--overview)
|
|
41
|
+
- [⚙️ Installation & Setup](#️-installation--setup)
|
|
42
|
+
- [🚀 Quickstart Tutorial](#-quickstart-tutorial)
|
|
43
|
+
- [🛠 How-to Guides](#-how-to-guides)
|
|
44
|
+
- [📖 API Reference](#-api-reference)
|
|
45
|
+
- [🔍 Troubleshooting & FAQ](#-troubleshooting--faq)
|
|
46
|
+
|
|
47
|
+
## ✨ Features
|
|
48
|
+
|
|
49
|
+
- Persistent storage with SQLite
|
|
50
|
+
- Automatic audit trails (who, when, what changed)
|
|
51
|
+
- Strong typing & validation via SQLModel
|
|
52
|
+
- Lifecycle hooks before/after insert, update, delete
|
|
53
|
+
- Soft delete with `deleted_at` fields
|
|
54
|
+
- ConnectRPC bundle generation with Create, Read, Update, Delete + audit
|
|
55
|
+
- Health & monitoring actions (audit log, schema diagram)
|
|
56
|
+
- Batch operations for insert/delete
|
|
57
|
+
- Session management with smart reuse & transactions
|
|
58
|
+
- Bootstrap system for one-command setup
|
|
59
|
+
- CSV export/import for backups and migration
|
|
60
|
+
- Database backup/restore with integrity checking
|
|
61
|
+
|
|
62
|
+
## 🧠 Concepts & Overview
|
|
63
|
+
|
|
64
|
+
Vention Storage is a component-based persistence layer for machine apps:
|
|
65
|
+
|
|
66
|
+
- **Database** → SQLite database with managed sessions and transactions
|
|
67
|
+
- **ModelAccessor** → Strongly-typed Create, Read, Update, Delete interface for your SQLModel classes
|
|
68
|
+
- **Hooks** → Functions that run before/after Create, Read, Update, Delete operations
|
|
69
|
+
- **AuditLog** → Automatically records all data mutations
|
|
70
|
+
- **RpcBundle** → Auto-generated ConnectRPC bundle with Create, Read, Update, Delete + database management actions
|
|
71
|
+
|
|
72
|
+
## ⚙️ Installation & Setup
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pip install vention-storage
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Optional dependencies:**
|
|
79
|
+
- sqlalchemy-schemadisplay and Graphviz → enable database schema visualization
|
|
80
|
+
|
|
81
|
+
MacOS:
|
|
82
|
+
```bash
|
|
83
|
+
brew install graphviz
|
|
84
|
+
pip install sqlalchemy-schemadisplay
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Linux (Debian/Ubuntu)
|
|
88
|
+
```bash
|
|
89
|
+
sudo apt-get install graphviz
|
|
90
|
+
pip install sqlalchemy-schemadisplay
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## 🚀 Quickstart Tutorial
|
|
94
|
+
|
|
95
|
+
Define a model, bootstrap storage, and get full Create, Read, Update, Delete RPC actions in minutes:
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from datetime import datetime
|
|
99
|
+
from typing import Optional
|
|
100
|
+
from sqlmodel import Field, SQLModel
|
|
101
|
+
from communication.app import VentionApp
|
|
102
|
+
from storage.bootstrap import bootstrap
|
|
103
|
+
from storage.accessor import ModelAccessor
|
|
104
|
+
from storage.vention_communication import build_storage_bundle
|
|
105
|
+
|
|
106
|
+
class User(SQLModel, table=True):
|
|
107
|
+
id: Optional[int] = Field(default=None, primary_key=True)
|
|
108
|
+
name: str
|
|
109
|
+
email: str
|
|
110
|
+
deleted_at: Optional[datetime] = Field(default=None, index=True)
|
|
111
|
+
|
|
112
|
+
# Initialize database
|
|
113
|
+
bootstrap(
|
|
114
|
+
database_url="sqlite:///./my_app.db",
|
|
115
|
+
create_tables=True
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
# Create accessor
|
|
119
|
+
user_accessor = ModelAccessor(User, "users")
|
|
120
|
+
|
|
121
|
+
# Build RPC bundle and add to app
|
|
122
|
+
app = VentionApp(name="my-app")
|
|
123
|
+
storage_bundle = build_storage_bundle(
|
|
124
|
+
accessors=[user_accessor],
|
|
125
|
+
max_records_per_model=100,
|
|
126
|
+
enable_db_actions=True
|
|
127
|
+
)
|
|
128
|
+
app.add_bundle(storage_bundle)
|
|
129
|
+
app.finalize()
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
➡️ You now have Create, Read, Update, Delete, audit, backup, and CSV actions available via ConnectRPC.
|
|
133
|
+
|
|
134
|
+
## 🛠 How-to Guides
|
|
135
|
+
|
|
136
|
+
### Bootstrap Multiple Models
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
# user_accessor was created earlier in the Quickstart example
|
|
140
|
+
# Reuse it here to bootstrap multiple models at once
|
|
141
|
+
|
|
142
|
+
product_accessor = ModelAccessor(Product, "products")
|
|
143
|
+
|
|
144
|
+
# Build bundle with multiple accessors
|
|
145
|
+
storage_bundle = build_storage_bundle(
|
|
146
|
+
accessors=[user_accessor, product_accessor],
|
|
147
|
+
max_records_per_model=100,
|
|
148
|
+
enable_db_actions=True
|
|
149
|
+
)
|
|
150
|
+
app.add_bundle(storage_bundle)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Export to CSV
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
# Using ConnectRPC client
|
|
157
|
+
from communication.client import ConnectClient
|
|
158
|
+
|
|
159
|
+
client = ConnectClient("http://localhost:8000")
|
|
160
|
+
response = await client.call("Database_ExportZip", {})
|
|
161
|
+
with open("backup.zip", "wb") as f:
|
|
162
|
+
f.write(response.data)
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Backup & Restore
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
# Backup
|
|
169
|
+
backup_response = await client.call("Database_BackupSqlite", {})
|
|
170
|
+
with open(backup_response.filename, "wb") as f:
|
|
171
|
+
f.write(backup_response.data)
|
|
172
|
+
|
|
173
|
+
# Restore
|
|
174
|
+
with open("backup.sqlite", "rb") as f:
|
|
175
|
+
restore_response = await client.call(
|
|
176
|
+
"Database_RestoreSqlite",
|
|
177
|
+
{
|
|
178
|
+
"bytes": f.read(),
|
|
179
|
+
"filename": "backup.sqlite",
|
|
180
|
+
"integrity_check": True,
|
|
181
|
+
"dry_run": False
|
|
182
|
+
}
|
|
183
|
+
)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Use Lifecycle Hooks
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
@user_accessor.before_insert()
|
|
190
|
+
def validate_email(session, instance):
|
|
191
|
+
if "@" not in instance.email:
|
|
192
|
+
raise ValueError("Invalid email")
|
|
193
|
+
|
|
194
|
+
@user_accessor.after_insert()
|
|
195
|
+
def log_creation(session, instance):
|
|
196
|
+
print(f"User created: {instance.name}")
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Query Audit Logs
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
from storage.auditor import AuditLog
|
|
203
|
+
from sqlmodel import select
|
|
204
|
+
|
|
205
|
+
with database.transaction() as session:
|
|
206
|
+
logs = session.exec(select(AuditLog).where(AuditLog.component == "users")).all()
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Using the model accessors
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
# Create
|
|
213
|
+
user = user_accessor.insert(User(name="Alice", email="alice@example.com"), actor="admin")
|
|
214
|
+
|
|
215
|
+
# Read
|
|
216
|
+
user = user_accessor.get(user.id)
|
|
217
|
+
|
|
218
|
+
# Update
|
|
219
|
+
user.name = "Alice Smith"
|
|
220
|
+
user_accessor.save(user, actor="admin")
|
|
221
|
+
|
|
222
|
+
# Delete
|
|
223
|
+
user_accessor.delete(user.id, actor="admin")
|
|
224
|
+
|
|
225
|
+
# Restore (for soft-deleted models)
|
|
226
|
+
user_accessor.restore(user.id, actor="admin")
|
|
227
|
+
|
|
228
|
+
# Find users by exact match
|
|
229
|
+
users = user_accessor.find(user_accessor.where.email == "alice@example.com")
|
|
230
|
+
|
|
231
|
+
# Multiple conditions (AND logic)
|
|
232
|
+
users = user_accessor.find(
|
|
233
|
+
user_accessor.where.name == "Alice",
|
|
234
|
+
user_accessor.where.email == "alice@example.com"
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
# Comparison operators
|
|
238
|
+
adults = user_accessor.find(user_accessor.where.age >= 18)
|
|
239
|
+
recent = user_accessor.find(user_accessor.where.created_at > cutoff_date)
|
|
240
|
+
|
|
241
|
+
# String operations
|
|
242
|
+
smiths = user_accessor.find(user_accessor.where.name.contains("Smith"))
|
|
243
|
+
gmail_users = user_accessor.find(user_accessor.where.email.endswith("@gmail.com"))
|
|
244
|
+
search = user_accessor.find(user_accessor.where.name.ilike("%alice%")) # case-insensitive
|
|
245
|
+
|
|
246
|
+
# Collection check
|
|
247
|
+
admins = user_accessor.find(user_accessor.where.role.in_(["admin", "superadmin"]))
|
|
248
|
+
|
|
249
|
+
# Null checks
|
|
250
|
+
unverified = user_accessor.find(user_accessor.where.verified_at.is_(None))
|
|
251
|
+
verified = user_accessor.find(user_accessor.where.verified_at.isnot(None))
|
|
252
|
+
|
|
253
|
+
# With pagination and sorting
|
|
254
|
+
page = user_accessor.find(
|
|
255
|
+
user_accessor.where.status == "active",
|
|
256
|
+
limit=10,
|
|
257
|
+
offset=20,
|
|
258
|
+
order_by="created_at",
|
|
259
|
+
order_desc=True
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
# Include soft-deleted records
|
|
263
|
+
all_users = user_accessor.find(
|
|
264
|
+
user_accessor.where.role == "admin",
|
|
265
|
+
include_deleted=True
|
|
266
|
+
)
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Using ConnectRPC Client
|
|
270
|
+
|
|
271
|
+
Once the bundle is added to your `VentionApp`, each `ModelAccessor` automatically exposes full CRUD actions via ConnectRPC.
|
|
272
|
+
|
|
273
|
+
Example: interacting with the `Users` RPC actions.
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
import { createPromiseClient } from "@connectrpc/connect";
|
|
277
|
+
import { createConnectTransport } from "@connectrpc/connect-web";
|
|
278
|
+
|
|
279
|
+
const transport = createConnectTransport({
|
|
280
|
+
baseUrl: "http://localhost:8000",
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
const client = createPromiseClient(YourServiceClient, transport);
|
|
284
|
+
|
|
285
|
+
// Create
|
|
286
|
+
export async function createUser(name: string, email: string) {
|
|
287
|
+
const res = await client.usersCreateRecord({
|
|
288
|
+
record: { name, email },
|
|
289
|
+
actor: "operator"
|
|
290
|
+
});
|
|
291
|
+
return res.record;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Read
|
|
295
|
+
export async function getUser(id: number) {
|
|
296
|
+
const res = await client.usersGetRecord({
|
|
297
|
+
recordId: id,
|
|
298
|
+
includeDeleted: false
|
|
299
|
+
});
|
|
300
|
+
return res.record;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Update
|
|
304
|
+
export async function updateUser(id: number, name: string) {
|
|
305
|
+
const res = await client.usersUpdateRecord({
|
|
306
|
+
recordId: id,
|
|
307
|
+
record: { name },
|
|
308
|
+
actor: "operator"
|
|
309
|
+
});
|
|
310
|
+
return res.record;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Delete (soft delete if model supports deleted_at)
|
|
314
|
+
export async function deleteUser(id: number) {
|
|
315
|
+
await client.usersDeleteRecord({
|
|
316
|
+
recordId: id,
|
|
317
|
+
actor: "operator"
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Restore
|
|
322
|
+
export async function restoreUser(id: number) {
|
|
323
|
+
const res = await client.usersRestoreRecord({
|
|
324
|
+
recordId: id,
|
|
325
|
+
actor: "operator"
|
|
326
|
+
});
|
|
327
|
+
return res.record;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// List
|
|
331
|
+
export async function listUsers() {
|
|
332
|
+
const res = await client.usersListRecords({
|
|
333
|
+
includeDeleted: false
|
|
334
|
+
});
|
|
335
|
+
return res.records;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Find by exact match
|
|
339
|
+
export async function findUserByEmail(email: string) {
|
|
340
|
+
const res = await client.usersFindRecords({
|
|
341
|
+
filters: [
|
|
342
|
+
{ field: "email", operation: "eq", value: email }
|
|
343
|
+
]
|
|
344
|
+
});
|
|
345
|
+
return res.records;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Find with multiple conditions (AND logic)
|
|
349
|
+
export async function findActiveAdmins() {
|
|
350
|
+
const res = await client.usersFindRecords({
|
|
351
|
+
filters: [
|
|
352
|
+
{ field: "role", operation: "eq", value: "admin" },
|
|
353
|
+
{ field: "age", operation: "gte", value: "18" }
|
|
354
|
+
]
|
|
355
|
+
});
|
|
356
|
+
return res.records;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// Find with null checks
|
|
360
|
+
export async function findUnverifiedUsers() {
|
|
361
|
+
const res = await client.usersFindRecords({
|
|
362
|
+
filters: [
|
|
363
|
+
{ field: "verified_at", operation: "is_null" }
|
|
364
|
+
]
|
|
365
|
+
});
|
|
366
|
+
return res.records;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// Complex query example
|
|
370
|
+
export async function findRecentPremiumUsers(cutoffDate: string) {
|
|
371
|
+
const res = await client.usersFindRecords({
|
|
372
|
+
filters: [
|
|
373
|
+
{ field: "subscription", operation: "in", value: ["premium", "enterprise"] },
|
|
374
|
+
{ field: "created_at", operation: "gte", value: cutoffDate },
|
|
375
|
+
{ field: "email_verified", operation: "is_not_null" }
|
|
376
|
+
],
|
|
377
|
+
limit: 50,
|
|
378
|
+
orderBy: "created_at",
|
|
379
|
+
orderDesc: true
|
|
380
|
+
});
|
|
381
|
+
return res.records;
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### Filter Operations Reference
|
|
386
|
+
|
|
387
|
+
| Operation | Description |
|
|
388
|
+
|----------------|--------------------------------------|
|
|
389
|
+
| `eq` | Exact match |
|
|
390
|
+
| `ne` | Not equal to value |
|
|
391
|
+
| `gt` | Greater than value |
|
|
392
|
+
| `gte` | Greater than or equal |
|
|
393
|
+
| `lt` | Less than value |
|
|
394
|
+
| `lte` | Less than or equal |
|
|
395
|
+
| `in` | Value in array |
|
|
396
|
+
| `not_in` | Value not in array |
|
|
397
|
+
| `contains` | Field contains substring |
|
|
398
|
+
| `starts_with` | Field starts with prefix |
|
|
399
|
+
| `ends_with` | Field ends with suffix |
|
|
400
|
+
| `like` | Case-insensitive pattern match |
|
|
401
|
+
| `is_null` | Field is null (no value needed) |
|
|
402
|
+
| `is_not_null` | Field is not null (no value needed) |
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
## 📖 API Reference
|
|
406
|
+
|
|
407
|
+
### bootstrap
|
|
408
|
+
|
|
409
|
+
```python
|
|
410
|
+
def bootstrap(
|
|
411
|
+
*,
|
|
412
|
+
database_url: Optional[str] = None,
|
|
413
|
+
create_tables: bool = True,
|
|
414
|
+
) -> None
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
Initialize the database engine and optionally create tables. This function performs environment setup only.
|
|
418
|
+
|
|
419
|
+
### build_storage_bundle
|
|
420
|
+
|
|
421
|
+
```python
|
|
422
|
+
def build_storage_bundle(
|
|
423
|
+
*,
|
|
424
|
+
accessors: Sequence[ModelAccessor[Any]],
|
|
425
|
+
max_records_per_model: Optional[int] = 5,
|
|
426
|
+
enable_db_actions: bool = True,
|
|
427
|
+
) -> RpcBundle
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
Build a ConnectRPC RpcBundle exposing CRUD and database utilities. Returns an `RpcBundle` that can be added to a `VentionApp` using `app.add_bundle()`.
|
|
431
|
+
|
|
432
|
+
### ModelAccessor
|
|
433
|
+
|
|
434
|
+
```python
|
|
435
|
+
ModelAccessor(
|
|
436
|
+
model: Type[ModelType],
|
|
437
|
+
component_name: str,
|
|
438
|
+
*,
|
|
439
|
+
enable_auditing: bool = True,
|
|
440
|
+
)
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
**Read**
|
|
444
|
+
- `get(id, include_deleted=False) -> Optional[ModelType]`
|
|
445
|
+
- `all(include_deleted=False) -> List[ModelType]`
|
|
446
|
+
|
|
447
|
+
**Write**
|
|
448
|
+
- `insert(obj, actor="internal") -> ModelType`
|
|
449
|
+
- `save(obj, actor="internal") -> ModelType`
|
|
450
|
+
- `delete(id, actor="internal") -> bool`
|
|
451
|
+
- `restore(id, actor="internal") -> bool`
|
|
452
|
+
|
|
453
|
+
**Batch**
|
|
454
|
+
- `insert_many(objs, actor="internal") -> List[ModelType]`
|
|
455
|
+
- `delete_many(ids, actor="internal") -> int`
|
|
456
|
+
|
|
457
|
+
**Hooks**
|
|
458
|
+
- `@accessor.before_insert()`
|
|
459
|
+
- `@accessor.after_insert()`
|
|
460
|
+
- `@accessor.before_update()`
|
|
461
|
+
- `@accessor.after_update()`
|
|
462
|
+
- `@accessor.before_delete()`
|
|
463
|
+
- `@accessor.after_delete()`
|
|
464
|
+
|
|
465
|
+
**Parameters**
|
|
466
|
+
- `enable_auditing`: If `False`, disables audit logging for this accessor. Useful for models that shouldn't be audited (e.g., audit logs themselves). Defaults to `True`.
|
|
467
|
+
|
|
468
|
+
### Database Helpers
|
|
469
|
+
|
|
470
|
+
- `database.set_database_url(url: str) -> None`
|
|
471
|
+
- `database.get_engine() -> Engine`
|
|
472
|
+
- `database.transaction() -> Iterator[Session]`
|
|
473
|
+
- `database.use_session(session: Optional[Session] = None) -> Iterator[Session]`
|
|
474
|
+
|
|
475
|
+
### AuditLog model
|
|
476
|
+
|
|
477
|
+
```python
|
|
478
|
+
class AuditLog(SQLModel, table=True):
|
|
479
|
+
id: int
|
|
480
|
+
timestamp: datetime
|
|
481
|
+
component: str
|
|
482
|
+
record_id: int
|
|
483
|
+
operation: str
|
|
484
|
+
actor: str
|
|
485
|
+
before: Optional[Dict[str, Any]]
|
|
486
|
+
after: Optional[Dict[str, Any]]
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
## 🔍 Troubleshooting & FAQ
|
|
490
|
+
|
|
491
|
+
- **Diagram endpoint fails** → Ensure Graphviz + sqlalchemy-schemadisplay are installed.
|
|
492
|
+
- **No audit actor shown** → Provide X-User header in API requests.
|
|
493
|
+
- **Soft delete not working** → Your model must have a `deleted_at` field.
|
|
494
|
+
- **Restore fails** → Ensure `integrity_check=True` passes when restoring backups.
|