sqlobjects 0.1.0__py3-none-any.whl
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/__init__.py +38 -0
- sqlobjects/config.py +519 -0
- sqlobjects/database.py +586 -0
- sqlobjects/exceptions.py +538 -0
- sqlobjects/expressions.py +1054 -0
- sqlobjects/fields.py +1866 -0
- sqlobjects/history.py +101 -0
- sqlobjects/metadata.py +1130 -0
- sqlobjects/model.py +1009 -0
- sqlobjects/objects.py +812 -0
- sqlobjects/queries.py +1059 -0
- sqlobjects/relations.py +843 -0
- sqlobjects/session.py +389 -0
- sqlobjects/signals.py +464 -0
- sqlobjects/utils/__init__.py +5 -0
- sqlobjects/utils/naming.py +53 -0
- sqlobjects/utils/pattern.py +644 -0
- sqlobjects/validators.py +294 -0
- sqlobjects-0.1.0.dist-info/METADATA +29 -0
- sqlobjects-0.1.0.dist-info/RECORD +23 -0
- sqlobjects-0.1.0.dist-info/WHEEL +5 -0
- sqlobjects-0.1.0.dist-info/licenses/LICENSE +21 -0
- sqlobjects-0.1.0.dist-info/top_level.txt +1 -0
sqlobjects/database.py
ADDED
|
@@ -0,0 +1,586 @@
|
|
|
1
|
+
from collections.abc import Mapping
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Any, Protocol
|
|
4
|
+
|
|
5
|
+
from sqlalchemy.ext.asyncio import AsyncEngine, create_async_engine
|
|
6
|
+
|
|
7
|
+
from .session import SessionContextManager
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"DatabaseConfig",
|
|
12
|
+
"Database",
|
|
13
|
+
"DatabaseManager",
|
|
14
|
+
"DatabaseObserver",
|
|
15
|
+
"init_db",
|
|
16
|
+
"init_dbs",
|
|
17
|
+
"create_tables",
|
|
18
|
+
"drop_tables",
|
|
19
|
+
"close_db",
|
|
20
|
+
"close_dbs",
|
|
21
|
+
"close_all_dbs",
|
|
22
|
+
"set_default_db",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class DatabaseObserver(Protocol):
|
|
27
|
+
"""Database event observer protocol"""
|
|
28
|
+
|
|
29
|
+
def on_database_added(self, name: str, database: "Database", is_default: bool) -> None: ...
|
|
30
|
+
def on_database_closed(self, name: str) -> None: ...
|
|
31
|
+
def on_default_changed(self, old_default: str | None, new_default: str | None) -> None: ...
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(init=False)
|
|
35
|
+
class DatabaseConfig:
|
|
36
|
+
"""Database configuration class
|
|
37
|
+
|
|
38
|
+
Uses dataclass to automatically generate initialization and other methods.
|
|
39
|
+
|
|
40
|
+
Attributes:
|
|
41
|
+
url: Database connection URL
|
|
42
|
+
echo: Whether to print SQL statements
|
|
43
|
+
pool_size: Connection pool size
|
|
44
|
+
max_overflow: Maximum pool overflow count
|
|
45
|
+
pool_timeout: Connection timeout in seconds
|
|
46
|
+
pool_recycle: Connection recycle time in seconds
|
|
47
|
+
engine_kwargs: Additional SQLAlchemy engine parameters
|
|
48
|
+
|
|
49
|
+
Examples:
|
|
50
|
+
>>> config = DatabaseConfig(
|
|
51
|
+
... url="postgresql+asyncpg://user:pass@localhost/mydb",
|
|
52
|
+
... pool_size=10,
|
|
53
|
+
... echo=True,
|
|
54
|
+
... isolation_level="READ_COMMITTED",
|
|
55
|
+
... )
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
url: str
|
|
59
|
+
echo: bool
|
|
60
|
+
pool_size: int
|
|
61
|
+
max_overflow: int
|
|
62
|
+
pool_timeout: int
|
|
63
|
+
pool_recycle: int
|
|
64
|
+
engine_kwargs: dict[str, Any]
|
|
65
|
+
|
|
66
|
+
def __init__(
|
|
67
|
+
self,
|
|
68
|
+
url: str,
|
|
69
|
+
echo: bool = False,
|
|
70
|
+
pool_size: int = 5,
|
|
71
|
+
max_overflow: int = 10,
|
|
72
|
+
pool_timeout: int = 30,
|
|
73
|
+
pool_recycle: int = 3600,
|
|
74
|
+
**kwargs: Any,
|
|
75
|
+
) -> None:
|
|
76
|
+
"""Initialize database configuration
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
url: Database connection URL
|
|
80
|
+
echo: Whether to print SQL statements
|
|
81
|
+
pool_size: Connection pool size
|
|
82
|
+
max_overflow: Maximum pool overflow count
|
|
83
|
+
pool_timeout: Connection timeout in seconds
|
|
84
|
+
pool_recycle: Connection recycle time in seconds
|
|
85
|
+
**kwargs: Additional SQLAlchemy engine parameters
|
|
86
|
+
"""
|
|
87
|
+
self.url = url
|
|
88
|
+
self.echo = echo
|
|
89
|
+
self.pool_size = pool_size
|
|
90
|
+
self.max_overflow = max_overflow
|
|
91
|
+
self.pool_timeout = pool_timeout
|
|
92
|
+
self.pool_recycle = pool_recycle
|
|
93
|
+
self.engine_kwargs = kwargs
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class Database:
|
|
97
|
+
"""Single database connection with event handling and table operations
|
|
98
|
+
|
|
99
|
+
Represents a single database connection, providing unified event registration
|
|
100
|
+
interface and table operation methods.
|
|
101
|
+
|
|
102
|
+
Attributes:
|
|
103
|
+
name: Unique name for the database connection
|
|
104
|
+
config: Database configuration
|
|
105
|
+
engine: SQLAlchemy async engine
|
|
106
|
+
|
|
107
|
+
Examples:
|
|
108
|
+
>>> config = DatabaseConfig(url="sqlite+aiosqlite:///test.db")
|
|
109
|
+
>>> db = Database("main", config)
|
|
110
|
+
>>> @db.on("connect")
|
|
111
|
+
... def on_connect(conn, record):
|
|
112
|
+
... print("Database connected")
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
def __init__(self, name: str, config: DatabaseConfig) -> None:
|
|
116
|
+
"""Initialize database instance
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
name: Unique name for the database connection
|
|
120
|
+
config: Database configuration
|
|
121
|
+
"""
|
|
122
|
+
self.name = name
|
|
123
|
+
self.config = config
|
|
124
|
+
|
|
125
|
+
# Build engine parameters
|
|
126
|
+
engine_kwargs: dict[str, Any] = {
|
|
127
|
+
"echo": config.echo,
|
|
128
|
+
**config.engine_kwargs,
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
# Add connection pool parameters for non-SQLite databases
|
|
132
|
+
if not config.url.startswith("sqlite"):
|
|
133
|
+
engine_kwargs.update(
|
|
134
|
+
{
|
|
135
|
+
"pool_size": config.pool_size,
|
|
136
|
+
"max_overflow": config.max_overflow,
|
|
137
|
+
"pool_timeout": config.pool_timeout,
|
|
138
|
+
"pool_recycle": config.pool_recycle,
|
|
139
|
+
}
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
# Create async engine
|
|
143
|
+
self.engine: AsyncEngine = create_async_engine(config.url, **engine_kwargs)
|
|
144
|
+
|
|
145
|
+
def on(self, event_name: str, target=None):
|
|
146
|
+
"""Unified event registration method
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
event_name: Event name (connect, close, before_commit, etc.)
|
|
150
|
+
target: Event target, automatically selected by default
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
SQLAlchemy event listener decorator
|
|
154
|
+
|
|
155
|
+
Examples:
|
|
156
|
+
>>> @db.on("connect")
|
|
157
|
+
... def on_connect(conn, record):
|
|
158
|
+
... print("Database connected")
|
|
159
|
+
|
|
160
|
+
>>> @db.on("before_commit")
|
|
161
|
+
... def before_commit(conn):
|
|
162
|
+
... print("About to commit transaction")
|
|
163
|
+
"""
|
|
164
|
+
from sqlalchemy import event
|
|
165
|
+
|
|
166
|
+
# Automatically select event target
|
|
167
|
+
if target is None:
|
|
168
|
+
target = self.engine.sync_engine
|
|
169
|
+
|
|
170
|
+
return event.listens_for(target, event_name)
|
|
171
|
+
|
|
172
|
+
async def create_tables(self, metadata) -> None:
|
|
173
|
+
"""Create all tables defined in the metadata
|
|
174
|
+
|
|
175
|
+
Creates all tables, indexes, and constraints defined in the provided
|
|
176
|
+
SQLAlchemy metadata object using the database engine.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
metadata: SQLAlchemy metadata object containing table definitions
|
|
180
|
+
|
|
181
|
+
Examples:
|
|
182
|
+
>>> from sqlobjects.base import ObjectModel
|
|
183
|
+
>>> await db.create_tables(ObjectModel.__registry__)
|
|
184
|
+
"""
|
|
185
|
+
async with self.engine.begin() as conn:
|
|
186
|
+
await conn.run_sync(metadata.create_all)
|
|
187
|
+
|
|
188
|
+
async def drop_tables(self, metadata) -> None:
|
|
189
|
+
"""Drop all tables defined in the metadata
|
|
190
|
+
|
|
191
|
+
Drops all tables, indexes, and constraints defined in the provided
|
|
192
|
+
SQLAlchemy metadata object from the database.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
metadata: SQLAlchemy metadata object containing table definitions
|
|
196
|
+
|
|
197
|
+
Examples:
|
|
198
|
+
>>> from sqlobjects.base import ObjectModel
|
|
199
|
+
>>> await db.drop_tables(ObjectModel.__registry__)
|
|
200
|
+
"""
|
|
201
|
+
async with self.engine.begin() as conn:
|
|
202
|
+
await conn.run_sync(metadata.drop_all)
|
|
203
|
+
|
|
204
|
+
async def disconnect(self) -> None:
|
|
205
|
+
"""Disconnect database and clean up resources
|
|
206
|
+
|
|
207
|
+
Properly disposes the SQLAlchemy engine and closes all connections.
|
|
208
|
+
Should be called when the database is no longer needed.
|
|
209
|
+
"""
|
|
210
|
+
await self.engine.dispose()
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
class DatabaseManager:
|
|
214
|
+
"""Multi-database connection manager
|
|
215
|
+
|
|
216
|
+
Manages multiple database connections, handles default database selection,
|
|
217
|
+
provides table operations and connection lifecycle management.
|
|
218
|
+
Uses observer pattern to decouple from ConnectionContextManager.
|
|
219
|
+
|
|
220
|
+
Examples:
|
|
221
|
+
>>> manager = DatabaseManager()
|
|
222
|
+
>>> await manager.add_database("main", main_config, is_default=True)
|
|
223
|
+
>>> await manager.add_database("analytics", analytics_config)
|
|
224
|
+
>>> await manager.create_tables(ObjectModel, "main")
|
|
225
|
+
"""
|
|
226
|
+
|
|
227
|
+
def __init__(self) -> None:
|
|
228
|
+
"""Initialize database manager"""
|
|
229
|
+
self._databases: dict[str, Database] = {}
|
|
230
|
+
self._default_db: str | None = None
|
|
231
|
+
self._observers: list[DatabaseObserver] = []
|
|
232
|
+
|
|
233
|
+
def _notify_observers(self, event: str, **kwargs) -> None:
|
|
234
|
+
"""Notify all observers"""
|
|
235
|
+
for observer in self._observers:
|
|
236
|
+
getattr(observer, event)(**kwargs)
|
|
237
|
+
|
|
238
|
+
def add_observer(self, observer: DatabaseObserver) -> None:
|
|
239
|
+
"""Add database event observer
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
observer: Observer instance implementing DatabaseObserver protocol
|
|
243
|
+
"""
|
|
244
|
+
self._observers.append(observer)
|
|
245
|
+
|
|
246
|
+
def remove_observer(self, observer: DatabaseObserver) -> None:
|
|
247
|
+
"""Remove database event observer
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
observer: Observer instance to remove
|
|
251
|
+
|
|
252
|
+
Raises:
|
|
253
|
+
ValueError: When observer is not found in the list
|
|
254
|
+
"""
|
|
255
|
+
self._observers.remove(observer)
|
|
256
|
+
|
|
257
|
+
async def add_database(self, name: str, config: DatabaseConfig, is_default: bool = False) -> Database:
|
|
258
|
+
"""Add database connection
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
name: Unique database name
|
|
262
|
+
config: Database configuration
|
|
263
|
+
is_default: Whether to set as default database
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
Created database instance
|
|
267
|
+
|
|
268
|
+
Raises:
|
|
269
|
+
ValueError: When database connection fails
|
|
270
|
+
"""
|
|
271
|
+
try:
|
|
272
|
+
database = Database(name, config)
|
|
273
|
+
self._databases[name] = database
|
|
274
|
+
|
|
275
|
+
old_default = self._default_db
|
|
276
|
+
if is_default:
|
|
277
|
+
self._default_db = name
|
|
278
|
+
|
|
279
|
+
# Notify observers
|
|
280
|
+
self._notify_observers("on_database_added", name=name, database=database, is_default=is_default)
|
|
281
|
+
if is_default and old_default != name:
|
|
282
|
+
self._notify_observers("on_default_changed", old_default=old_default, new_default=name)
|
|
283
|
+
|
|
284
|
+
return database
|
|
285
|
+
except Exception as e:
|
|
286
|
+
raise ValueError(f"Failed to connect to database '{name}': {e}") from e
|
|
287
|
+
|
|
288
|
+
def get_database(self, db_name: str | None = None) -> Database:
|
|
289
|
+
"""Get database instance
|
|
290
|
+
|
|
291
|
+
Args:
|
|
292
|
+
db_name: Database name, uses default database when None
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
Database instance
|
|
296
|
+
|
|
297
|
+
Raises:
|
|
298
|
+
ValueError: When database does not exist
|
|
299
|
+
"""
|
|
300
|
+
name = db_name or self._default_db
|
|
301
|
+
if not name or name not in self._databases:
|
|
302
|
+
raise ValueError(f"Database '{name}' not found")
|
|
303
|
+
return self._databases[name]
|
|
304
|
+
|
|
305
|
+
async def create_tables(self, base_class, db_name: str | None = None) -> None:
|
|
306
|
+
"""Create all tables defined in the base class registry
|
|
307
|
+
|
|
308
|
+
Creates all tables, indexes, and constraints for models registered
|
|
309
|
+
in the base class registry using the specified database connection.
|
|
310
|
+
|
|
311
|
+
Args:
|
|
312
|
+
base_class: SQLObjects base class containing model registry
|
|
313
|
+
db_name: Database name to use, uses default database if None
|
|
314
|
+
|
|
315
|
+
Raises:
|
|
316
|
+
ValueError: When specified database does not exist
|
|
317
|
+
|
|
318
|
+
Examples:
|
|
319
|
+
>>> from sqlobjects.base import ObjectModel
|
|
320
|
+
>>> await manager.create_tables(ObjectModel)
|
|
321
|
+
>>> await manager.create_tables(ObjectModel, "analytics")
|
|
322
|
+
"""
|
|
323
|
+
database = self.get_database(db_name)
|
|
324
|
+
await database.create_tables(base_class.__registry__)
|
|
325
|
+
|
|
326
|
+
async def drop_tables(self, base_class, db_name: str | None = None) -> None:
|
|
327
|
+
"""Drop all tables defined in the base class registry
|
|
328
|
+
|
|
329
|
+
Drops all tables, indexes, and constraints for models registered
|
|
330
|
+
in the base class registry from the specified database connection.
|
|
331
|
+
|
|
332
|
+
Args:
|
|
333
|
+
base_class: SQLObjects base class containing model registry
|
|
334
|
+
db_name: Database name to use, uses default database if None
|
|
335
|
+
|
|
336
|
+
Raises:
|
|
337
|
+
ValueError: When specified database does not exist
|
|
338
|
+
|
|
339
|
+
Examples:
|
|
340
|
+
>>> from sqlobjects.base import ObjectModel
|
|
341
|
+
>>> await manager.drop_tables(ObjectModel)
|
|
342
|
+
>>> await manager.drop_tables(ObjectModel, "analytics")
|
|
343
|
+
"""
|
|
344
|
+
database = self.get_database(db_name)
|
|
345
|
+
await database.drop_tables(base_class.__registry__)
|
|
346
|
+
|
|
347
|
+
async def close(self, db_name: str | None = None, auto_default: bool = False) -> None:
|
|
348
|
+
"""Close database connection and clean up resources
|
|
349
|
+
|
|
350
|
+
Closes the specified database connection and removes it from the manager.
|
|
351
|
+
Handles default database reassignment when closing the default database.
|
|
352
|
+
|
|
353
|
+
Args:
|
|
354
|
+
db_name: Database name to close, closes default when None
|
|
355
|
+
auto_default: Whether to automatically select new default when closing default database
|
|
356
|
+
|
|
357
|
+
Raises:
|
|
358
|
+
ValueError: When specified database does not exist
|
|
359
|
+
"""
|
|
360
|
+
# Determine target database name
|
|
361
|
+
target_db = db_name or self._default_db
|
|
362
|
+
if not target_db or target_db not in self._databases:
|
|
363
|
+
raise ValueError(f"Database '{target_db}' not found")
|
|
364
|
+
|
|
365
|
+
# Close specified database
|
|
366
|
+
await self._databases[target_db].engine.dispose()
|
|
367
|
+
del self._databases[target_db]
|
|
368
|
+
self._notify_observers("on_database_closed", name=target_db)
|
|
369
|
+
|
|
370
|
+
# Handle default database change if closing default
|
|
371
|
+
if self._default_db == target_db:
|
|
372
|
+
old_default = self._default_db
|
|
373
|
+
if auto_default:
|
|
374
|
+
self._default_db = next(iter(self._databases), None)
|
|
375
|
+
else:
|
|
376
|
+
self._default_db = None
|
|
377
|
+
self._notify_observers("on_default_changed", old_default=old_default, new_default=self._default_db)
|
|
378
|
+
|
|
379
|
+
async def close_all(self) -> None:
|
|
380
|
+
"""Close all database connections and clean up resources
|
|
381
|
+
|
|
382
|
+
Closes all managed database connections, disposes their engines,
|
|
383
|
+
and resets the manager to initial state.
|
|
384
|
+
"""
|
|
385
|
+
for name, db in self._databases.items():
|
|
386
|
+
await db.engine.dispose()
|
|
387
|
+
self._notify_observers("on_database_closed", name=name)
|
|
388
|
+
|
|
389
|
+
old_default = self._default_db
|
|
390
|
+
self._databases.clear()
|
|
391
|
+
self._default_db = None
|
|
392
|
+
if old_default:
|
|
393
|
+
self._notify_observers("on_default_changed", old_default=old_default, new_default=None)
|
|
394
|
+
|
|
395
|
+
def set_default_db(self, db_name: str) -> None:
|
|
396
|
+
"""Set default database for operations
|
|
397
|
+
|
|
398
|
+
Changes the default database used when no specific database
|
|
399
|
+
name is provided in operations.
|
|
400
|
+
|
|
401
|
+
Args:
|
|
402
|
+
db_name: Database name to set as default
|
|
403
|
+
|
|
404
|
+
Raises:
|
|
405
|
+
ValueError: When database does not exist
|
|
406
|
+
"""
|
|
407
|
+
if db_name not in self._databases:
|
|
408
|
+
raise ValueError(f"Database '{db_name}' not found")
|
|
409
|
+
|
|
410
|
+
old_default = self._default_db
|
|
411
|
+
self._default_db = db_name
|
|
412
|
+
self._notify_observers("on_default_changed", old_default=old_default, new_default=db_name)
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
# Global database manager instance
|
|
416
|
+
_manager = DatabaseManager()
|
|
417
|
+
|
|
418
|
+
# Register TransactionContextManager as observer
|
|
419
|
+
_manager.add_observer(SessionContextManager)
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
async def init_db(
|
|
423
|
+
url: str,
|
|
424
|
+
name: str | None = None,
|
|
425
|
+
echo: bool = False,
|
|
426
|
+
pool_size: int = 5,
|
|
427
|
+
max_overflow: int = 10,
|
|
428
|
+
pool_timeout: int = 30,
|
|
429
|
+
pool_recycle: int = 3600,
|
|
430
|
+
is_default: bool = True,
|
|
431
|
+
**engine_kwargs: Any,
|
|
432
|
+
) -> Database:
|
|
433
|
+
"""Initialize single database connection.
|
|
434
|
+
|
|
435
|
+
Args:
|
|
436
|
+
url: Database URL (e.g., 'sqlite+aiosqlite:///db.sqlite', 'postgresql+asyncpg://user:pass@host/db')
|
|
437
|
+
name: Name for the database connection, uses "default" if None
|
|
438
|
+
echo: Whether to log all SQL statements
|
|
439
|
+
pool_size: Number of connections to maintain in the pool
|
|
440
|
+
max_overflow: Maximum number of connections that can overflow the pool
|
|
441
|
+
pool_timeout: Timeout in seconds for getting connection from pool
|
|
442
|
+
pool_recycle: Time in seconds to recycle connections
|
|
443
|
+
is_default: Whether this database should be set as the default database
|
|
444
|
+
**engine_kwargs: Additional SQLAlchemy engine arguments
|
|
445
|
+
|
|
446
|
+
Returns:
|
|
447
|
+
Database instance with configured connection
|
|
448
|
+
|
|
449
|
+
Raises:
|
|
450
|
+
ValueError: If database URL format is invalid
|
|
451
|
+
DatabaseError: If connection to database fails
|
|
452
|
+
ImportError: If required database driver is not installed
|
|
453
|
+
"""
|
|
454
|
+
config = DatabaseConfig(
|
|
455
|
+
url=url,
|
|
456
|
+
echo=echo,
|
|
457
|
+
pool_size=pool_size,
|
|
458
|
+
max_overflow=max_overflow,
|
|
459
|
+
pool_timeout=pool_timeout,
|
|
460
|
+
pool_recycle=pool_recycle,
|
|
461
|
+
**engine_kwargs,
|
|
462
|
+
)
|
|
463
|
+
db_name = name if name is not None else "default"
|
|
464
|
+
return await _manager.add_database(db_name, config, is_default=is_default)
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
async def init_dbs(
|
|
468
|
+
databases: Mapping[str, dict[str, Any] | DatabaseConfig],
|
|
469
|
+
default: str | None = None,
|
|
470
|
+
) -> tuple[Database, ...]:
|
|
471
|
+
"""Initialize multiple database connections.
|
|
472
|
+
|
|
473
|
+
Args:
|
|
474
|
+
databases: Dictionary mapping database names to their configurations
|
|
475
|
+
default: Name of the default database to use when none is specified, or None for no default
|
|
476
|
+
|
|
477
|
+
Returns:
|
|
478
|
+
Tuple of Database instances in the order they appear in the databases dict
|
|
479
|
+
|
|
480
|
+
Raises:
|
|
481
|
+
ValueError: If default database name is not in databases dict or URL format is invalid
|
|
482
|
+
DatabaseError: If connection to any database fails
|
|
483
|
+
ImportError: If required database drivers are not installed
|
|
484
|
+
"""
|
|
485
|
+
db_instances = []
|
|
486
|
+
|
|
487
|
+
for name, config_data in databases.items():
|
|
488
|
+
if isinstance(config_data, DatabaseConfig):
|
|
489
|
+
config = config_data
|
|
490
|
+
else:
|
|
491
|
+
config = DatabaseConfig(**config_data)
|
|
492
|
+
|
|
493
|
+
is_default = default is not None and name == default
|
|
494
|
+
database = await _manager.add_database(name, config, is_default)
|
|
495
|
+
db_instances.append(database)
|
|
496
|
+
|
|
497
|
+
return tuple(db_instances)
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
async def create_tables(base_class, db_name: str | None = None) -> None:
|
|
501
|
+
"""Create all tables defined in the base class registry
|
|
502
|
+
|
|
503
|
+
Creates all tables, indexes, and constraints for models registered
|
|
504
|
+
in the base class registry using the specified database connection.
|
|
505
|
+
|
|
506
|
+
Args:
|
|
507
|
+
base_class: SQLObjects base class containing model registry
|
|
508
|
+
db_name: Name of the database, uses default if None
|
|
509
|
+
|
|
510
|
+
Raises:
|
|
511
|
+
ValueError: When specified database does not exist
|
|
512
|
+
"""
|
|
513
|
+
await _manager.create_tables(base_class, db_name)
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
async def drop_tables(base_class, db_name: str | None = None) -> None:
|
|
517
|
+
"""Drop all tables defined in the base class registry
|
|
518
|
+
|
|
519
|
+
Drops all tables, indexes, and constraints for models registered
|
|
520
|
+
in the base class registry from the specified database connection.
|
|
521
|
+
|
|
522
|
+
Args:
|
|
523
|
+
base_class: SQLObjects base class containing model registry
|
|
524
|
+
db_name: Name of the database, uses default if None
|
|
525
|
+
|
|
526
|
+
Raises:
|
|
527
|
+
ValueError: When specified database does not exist
|
|
528
|
+
"""
|
|
529
|
+
await _manager.drop_tables(base_class, db_name)
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
async def close_db(db_name: str | None = None, auto_default: bool = False) -> None:
|
|
533
|
+
"""Close database connection and clean up resources
|
|
534
|
+
|
|
535
|
+
Closes the specified database connection and removes it from the manager.
|
|
536
|
+
Handles default database reassignment when closing the default database.
|
|
537
|
+
|
|
538
|
+
Args:
|
|
539
|
+
db_name: Name of specific database to close, closes default if None
|
|
540
|
+
auto_default: Whether to update default database when closing the default database
|
|
541
|
+
|
|
542
|
+
Raises:
|
|
543
|
+
ValueError: When specified database does not exist
|
|
544
|
+
"""
|
|
545
|
+
await _manager.close(db_name, auto_default)
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
async def close_dbs(db_names: list[str], auto_default: bool = False) -> None:
|
|
549
|
+
"""Close multiple specific database connections
|
|
550
|
+
|
|
551
|
+
Closes all specified database connections in sequence.
|
|
552
|
+
Handles default database reassignment if the default database is closed.
|
|
553
|
+
|
|
554
|
+
Args:
|
|
555
|
+
db_names: List of database names to close
|
|
556
|
+
auto_default: Whether to update default database when closing the default database
|
|
557
|
+
|
|
558
|
+
Raises:
|
|
559
|
+
ValueError: When any specified database does not exist
|
|
560
|
+
"""
|
|
561
|
+
for db_name in db_names:
|
|
562
|
+
await _manager.close(db_name, auto_default)
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
async def close_all_dbs() -> None:
|
|
566
|
+
"""Close all database connections and clean up resources
|
|
567
|
+
|
|
568
|
+
Closes all managed database connections, disposes their engines,
|
|
569
|
+
and resets the global manager to initial state.
|
|
570
|
+
"""
|
|
571
|
+
await _manager.close_all()
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
def set_default_db(db_name: str) -> None:
|
|
575
|
+
"""Set the default database by name
|
|
576
|
+
|
|
577
|
+
Changes the default database used when no specific database
|
|
578
|
+
name is provided in operations.
|
|
579
|
+
|
|
580
|
+
Args:
|
|
581
|
+
db_name: Name of the database to set as default
|
|
582
|
+
|
|
583
|
+
Raises:
|
|
584
|
+
ValueError: If database is not found
|
|
585
|
+
"""
|
|
586
|
+
_manager.set_default_db(db_name)
|