amsdal 0.5.18__cp312-cp312-macosx_10_13_universal2.whl → 0.5.20__cp312-cp312-macosx_10_13_universal2.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.
Potentially problematic release.
This version of amsdal might be problematic. Click here for more details.
- amsdal/__about__.py +1 -1
- amsdal/cloud/__init__.cpython-312-darwin.so +0 -0
- amsdal/cloud/client.cpython-312-darwin.so +0 -0
- amsdal/cloud/constants.cpython-312-darwin.so +0 -0
- amsdal/cloud/enums.cpython-312-darwin.so +0 -0
- amsdal/cloud/models/__init__.cpython-312-darwin.so +0 -0
- amsdal/cloud/models/base.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/__init__.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/actions/__init__.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/actions/add_allowlist_ip.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/actions/add_basic_auth.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/actions/add_dependency.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/actions/add_secret.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/actions/base.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/actions/create_deploy.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/actions/create_env.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/actions/create_session.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/actions/delete_allowlist_ip.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/actions/delete_basic_auth.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/actions/delete_dependency.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/actions/delete_env.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/actions/delete_secret.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/actions/destroy_deploy.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/actions/expose_db.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/actions/get_basic_auth_credentials.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/actions/get_monitoring_info.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/actions/list_dependencies.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/actions/list_deploys.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/actions/list_envs.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/actions/list_secrets.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/actions/manager.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/actions/signup_action.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/actions/update_deploy.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/auth/__init__.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/auth/base.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/auth/credentials.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/auth/manager.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/auth/signup_service.cpython-312-darwin.so +0 -0
- amsdal/cloud/services/auth/token.cpython-312-darwin.so +0 -0
- amsdal/contrib/__init__.cpython-312-darwin.so +0 -0
- amsdal/fixtures/__init__.cpython-312-darwin.so +0 -0
- amsdal/fixtures/manager.cpython-312-darwin.so +0 -0
- amsdal/fixtures/utils.cpython-312-darwin.so +0 -0
- amsdal/manager.cpython-312-darwin.so +0 -0
- amsdal/mixins/__init__.cpython-312-darwin.so +0 -0
- amsdal/mixins/class_versions_mixin.cpython-312-darwin.so +0 -0
- amsdal/schemas/manager.cpython-312-darwin.so +0 -0
- amsdal/services/__init__.py +11 -0
- amsdal/services/__init__.pyi +4 -0
- amsdal/services/external_connections.py +262 -0
- amsdal/services/external_connections.pyi +190 -0
- amsdal/services/external_model_generator.py +350 -0
- amsdal/services/external_model_generator.pyi +134 -0
- amsdal/services/transaction_execution.cpython-312-darwin.so +0 -0
- {amsdal-0.5.18.dist-info → amsdal-0.5.20.dist-info}/METADATA +1 -1
- {amsdal-0.5.18.dist-info → amsdal-0.5.20.dist-info}/RECORD +59 -55
- amsdal/services/__init__.cpython-312-darwin.so +0 -0
- {amsdal-0.5.18.dist-info → amsdal-0.5.20.dist-info}/WHEEL +0 -0
- {amsdal-0.5.18.dist-info → amsdal-0.5.20.dist-info}/licenses/LICENSE.txt +0 -0
- {amsdal-0.5.18.dist-info → amsdal-0.5.20.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
"""
|
|
2
|
+
External Model Generator Service.
|
|
3
|
+
|
|
4
|
+
Generates ExternalModel classes from external connection schemas.
|
|
5
|
+
This allows runtime model generation from external databases without
|
|
6
|
+
manual model definition.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from typing import Any
|
|
10
|
+
from typing import cast
|
|
11
|
+
|
|
12
|
+
from amsdal_data.connections.external.base import SchemaIntrospectionProtocol
|
|
13
|
+
from amsdal_models.classes.external_model import ExternalModel
|
|
14
|
+
from amsdal_models.utils.schema_converter import ExternalSchemaConverter
|
|
15
|
+
from amsdal_utils.schemas.schema import ObjectSchema
|
|
16
|
+
|
|
17
|
+
from amsdal.services.external_connections import ExternalConnectionManager
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ExternalModelGenerator:
|
|
21
|
+
"""
|
|
22
|
+
Service for generating ExternalModel classes from external connections.
|
|
23
|
+
|
|
24
|
+
This service introspects external database schemas and generates
|
|
25
|
+
corresponding ExternalModel classes that can be used immediately
|
|
26
|
+
for querying the external data.
|
|
27
|
+
|
|
28
|
+
Features:
|
|
29
|
+
- Automatic schema introspection
|
|
30
|
+
- Type mapping (SQL types -> Python types)
|
|
31
|
+
- Primary key detection
|
|
32
|
+
- In-memory model class generation
|
|
33
|
+
- No lakehouse schema creation
|
|
34
|
+
|
|
35
|
+
Example usage:
|
|
36
|
+
# Generate models for a single table
|
|
37
|
+
generator = ExternalModelGenerator()
|
|
38
|
+
User = generator.generate_model('external_db', 'users')
|
|
39
|
+
|
|
40
|
+
# Now use the generated model
|
|
41
|
+
users = User.objects.filter(active=True).execute()
|
|
42
|
+
|
|
43
|
+
# Generate models for all tables
|
|
44
|
+
models = generator.generate_models_for_connection('external_db')
|
|
45
|
+
User = models['User']
|
|
46
|
+
Post = models['Post']
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
def __init__(self) -> None:
|
|
50
|
+
self._connection_manager = ExternalConnectionManager()
|
|
51
|
+
self._schema_converter = ExternalSchemaConverter()
|
|
52
|
+
|
|
53
|
+
def generate_model(
|
|
54
|
+
self,
|
|
55
|
+
connection_name: str,
|
|
56
|
+
table_name: str,
|
|
57
|
+
model_name: str | None = None,
|
|
58
|
+
) -> type[ExternalModel]:
|
|
59
|
+
"""
|
|
60
|
+
Generate an ExternalModel class for a specific table.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
connection_name: Name of the external connection
|
|
64
|
+
table_name: Name of the table to generate model for
|
|
65
|
+
model_name: Optional custom model name (defaults to classified table name)
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
type[ExternalModel]: Generated model class ready to use
|
|
69
|
+
|
|
70
|
+
Raises:
|
|
71
|
+
ValueError: If connection doesn't support schema introspection
|
|
72
|
+
ConnectionError: If connection is not available
|
|
73
|
+
RuntimeError: If model generation fails
|
|
74
|
+
|
|
75
|
+
Example:
|
|
76
|
+
generator = ExternalModelGenerator()
|
|
77
|
+
User = generator.generate_model('external_db', 'users')
|
|
78
|
+
|
|
79
|
+
# Query using the generated model
|
|
80
|
+
active_users = User.objects.filter(active=True).execute()
|
|
81
|
+
"""
|
|
82
|
+
# Get the connection
|
|
83
|
+
connection = self._connection_manager.get_connection(connection_name)
|
|
84
|
+
|
|
85
|
+
# Check if connection supports schema introspection
|
|
86
|
+
if not isinstance(connection, SchemaIntrospectionProtocol): # type: ignore[misc]
|
|
87
|
+
msg = (
|
|
88
|
+
f"Connection '{connection_name}' does not support schema introspection. "
|
|
89
|
+
f'Connection type: {type(connection).__name__}'
|
|
90
|
+
)
|
|
91
|
+
raise ValueError(msg)
|
|
92
|
+
|
|
93
|
+
# Get table schema
|
|
94
|
+
table_schema = connection.get_table_schema(table_name)
|
|
95
|
+
|
|
96
|
+
# Convert to ObjectSchema
|
|
97
|
+
# Detect connection type and use appropriate converter
|
|
98
|
+
object_schema = self._convert_schema(
|
|
99
|
+
connection=connection,
|
|
100
|
+
table_name=table_name,
|
|
101
|
+
table_schema=table_schema,
|
|
102
|
+
connection_name=connection_name,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# Generate model class from ObjectSchema
|
|
106
|
+
model_class = self._create_model_class(object_schema, model_name)
|
|
107
|
+
|
|
108
|
+
return model_class
|
|
109
|
+
|
|
110
|
+
def generate_models_for_connection(
|
|
111
|
+
self,
|
|
112
|
+
connection_name: str,
|
|
113
|
+
table_names: list[str] | None = None,
|
|
114
|
+
) -> dict[str, type[ExternalModel]]:
|
|
115
|
+
"""
|
|
116
|
+
Generate ExternalModel classes for all tables in a connection.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
connection_name: Name of the external connection
|
|
120
|
+
table_names: Optional list of specific tables to generate models for.
|
|
121
|
+
If None, generates models for all tables.
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
dict[str, type[ExternalModel]]: Dictionary mapping model names to model classes
|
|
125
|
+
|
|
126
|
+
Raises:
|
|
127
|
+
ValueError: If connection doesn't support schema introspection
|
|
128
|
+
ConnectionError: If connection is not available
|
|
129
|
+
|
|
130
|
+
Example:
|
|
131
|
+
generator = ExternalModelGenerator()
|
|
132
|
+
models = generator.generate_models_for_connection('external_db')
|
|
133
|
+
|
|
134
|
+
# Access generated models
|
|
135
|
+
User = models['User']
|
|
136
|
+
Post = models['Post']
|
|
137
|
+
Comment = models['Comment']
|
|
138
|
+
|
|
139
|
+
# Or generate only specific tables
|
|
140
|
+
models = generator.generate_models_for_connection(
|
|
141
|
+
'external_db',
|
|
142
|
+
table_names=['users', 'posts']
|
|
143
|
+
)
|
|
144
|
+
"""
|
|
145
|
+
# Get the connection
|
|
146
|
+
connection = self._connection_manager.get_connection(connection_name)
|
|
147
|
+
|
|
148
|
+
# Check if connection supports schema introspection
|
|
149
|
+
if not isinstance(connection, SchemaIntrospectionProtocol): # type: ignore[misc]
|
|
150
|
+
msg = (
|
|
151
|
+
f"Connection '{connection_name}' does not support schema introspection. "
|
|
152
|
+
f'Connection type: {type(connection).__name__}'
|
|
153
|
+
)
|
|
154
|
+
raise ValueError(msg)
|
|
155
|
+
|
|
156
|
+
# Get list of tables
|
|
157
|
+
if table_names is None:
|
|
158
|
+
table_names = connection.get_table_names()
|
|
159
|
+
|
|
160
|
+
# Generate models for each table
|
|
161
|
+
models: dict[str, type[ExternalModel]] = {}
|
|
162
|
+
for table_name in table_names:
|
|
163
|
+
try:
|
|
164
|
+
model = self.generate_model(connection_name, table_name)
|
|
165
|
+
models[model.__name__] = model
|
|
166
|
+
except Exception as e:
|
|
167
|
+
# Log error but continue with other tables
|
|
168
|
+
print(f"Warning: Failed to generate model for table '{table_name}': {e}")
|
|
169
|
+
continue
|
|
170
|
+
|
|
171
|
+
return models
|
|
172
|
+
|
|
173
|
+
def _convert_schema(
|
|
174
|
+
self,
|
|
175
|
+
connection: Any,
|
|
176
|
+
table_name: str,
|
|
177
|
+
table_schema: list[dict[str, Any]],
|
|
178
|
+
connection_name: str,
|
|
179
|
+
) -> ObjectSchema:
|
|
180
|
+
"""
|
|
181
|
+
Convert raw table schema to ObjectSchema based on connection type.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
connection: The connection object
|
|
185
|
+
table_name: Name of the table
|
|
186
|
+
table_schema: Raw schema data from connection
|
|
187
|
+
connection_name: Name of the connection
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
ObjectSchema: Converted schema
|
|
191
|
+
"""
|
|
192
|
+
# Detect connection type and use appropriate converter
|
|
193
|
+
connection_type = type(connection).__name__
|
|
194
|
+
|
|
195
|
+
if 'sqlite' in connection_type.lower():
|
|
196
|
+
return self._schema_converter.sqlite_schema_to_object_schema(
|
|
197
|
+
table_name=table_name,
|
|
198
|
+
columns=table_schema,
|
|
199
|
+
connection_name=connection_name,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
# For other connection types, try to use generic converter
|
|
203
|
+
# First, try to detect the schema format
|
|
204
|
+
if table_schema and isinstance(table_schema[0], dict):
|
|
205
|
+
# Check if it's SQLite format (has 'cid', 'name', 'type', 'pk', etc.)
|
|
206
|
+
if all(key in table_schema[0] for key in ('cid', 'name', 'type')):
|
|
207
|
+
return self._schema_converter.sqlite_schema_to_object_schema(
|
|
208
|
+
table_name=table_name,
|
|
209
|
+
columns=table_schema,
|
|
210
|
+
connection_name=connection_name,
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
# Check if it's PostgreSQL format (has 'column_name', 'data_type', etc.)
|
|
214
|
+
if 'column_name' in table_schema[0] and 'data_type' in table_schema[0]:
|
|
215
|
+
return self._schema_converter.postgres_schema_to_object_schema(
|
|
216
|
+
table_name=table_name,
|
|
217
|
+
columns=table_schema,
|
|
218
|
+
connection_name=connection_name,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
# Try generic converter with format normalization
|
|
222
|
+
normalized_columns = self._normalize_schema_format(table_schema)
|
|
223
|
+
return self._schema_converter.generic_schema_to_object_schema(
|
|
224
|
+
table_name=table_name,
|
|
225
|
+
columns=normalized_columns,
|
|
226
|
+
connection_name=connection_name,
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
msg = f'Unknown schema format for connection type: {connection_type}'
|
|
230
|
+
raise ValueError(msg)
|
|
231
|
+
|
|
232
|
+
def _normalize_schema_format(self, table_schema: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
233
|
+
"""
|
|
234
|
+
Normalize various schema formats to generic format.
|
|
235
|
+
|
|
236
|
+
Converts various schema formats to the format expected by generic_schema_to_object_schema:
|
|
237
|
+
{'name': str, 'type': str, 'nullable': bool, 'primary_key': bool, 'default': Any}
|
|
238
|
+
"""
|
|
239
|
+
normalized = []
|
|
240
|
+
|
|
241
|
+
for column in table_schema:
|
|
242
|
+
# Try to extract name
|
|
243
|
+
name = column.get('name') or column.get('column_name') or column.get('field')
|
|
244
|
+
|
|
245
|
+
# Try to extract type
|
|
246
|
+
col_type = column.get('type') or column.get('data_type') or column.get('field_type') or 'TEXT'
|
|
247
|
+
|
|
248
|
+
# Try to extract nullable
|
|
249
|
+
nullable = True
|
|
250
|
+
if 'nullable' in column:
|
|
251
|
+
nullable = column['nullable']
|
|
252
|
+
elif 'is_nullable' in column:
|
|
253
|
+
nullable = column['is_nullable'] in (True, 'YES', 'yes', 1)
|
|
254
|
+
elif 'notnull' in column:
|
|
255
|
+
nullable = column['notnull'] in (False, 0)
|
|
256
|
+
|
|
257
|
+
# Try to extract primary key
|
|
258
|
+
pk = column.get('primary_key') or column.get('pk') or False
|
|
259
|
+
if isinstance(pk, int):
|
|
260
|
+
pk = pk > 0
|
|
261
|
+
|
|
262
|
+
# Try to extract default
|
|
263
|
+
default = column.get('default') or column.get('dflt_value') or column.get('column_default')
|
|
264
|
+
|
|
265
|
+
normalized.append(
|
|
266
|
+
{
|
|
267
|
+
'name': name,
|
|
268
|
+
'type': col_type,
|
|
269
|
+
'nullable': nullable,
|
|
270
|
+
'primary_key': pk,
|
|
271
|
+
'default': default,
|
|
272
|
+
}
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
return normalized
|
|
276
|
+
|
|
277
|
+
def _create_model_class(
|
|
278
|
+
self,
|
|
279
|
+
object_schema: ObjectSchema,
|
|
280
|
+
custom_name: str | None = None,
|
|
281
|
+
) -> type[ExternalModel]:
|
|
282
|
+
"""
|
|
283
|
+
Create an ExternalModel class from ObjectSchema.
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
object_schema: The schema to create model from
|
|
287
|
+
custom_name: Optional custom model name
|
|
288
|
+
|
|
289
|
+
Returns:
|
|
290
|
+
type[ExternalModel]: Generated model class
|
|
291
|
+
"""
|
|
292
|
+
# Extract model metadata from schema
|
|
293
|
+
model_name = custom_name or object_schema.title
|
|
294
|
+
table_name = cast(str, object_schema.__table_name__) # type: ignore[attr-defined]
|
|
295
|
+
connection_name = cast(str, object_schema.__connection__) # type: ignore[attr-defined]
|
|
296
|
+
pk_fields = getattr(object_schema, '__primary_key__', None)
|
|
297
|
+
|
|
298
|
+
# Build class attributes
|
|
299
|
+
class_attrs: dict[str, Any] = {
|
|
300
|
+
'__table_name__': table_name,
|
|
301
|
+
'__connection__': connection_name,
|
|
302
|
+
'__module__': __name__,
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
# Add primary key if present
|
|
306
|
+
if pk_fields:
|
|
307
|
+
# For composite keys, use list; for single key, use string
|
|
308
|
+
if len(pk_fields) == 1:
|
|
309
|
+
class_attrs['__primary_key__'] = pk_fields[0]
|
|
310
|
+
else:
|
|
311
|
+
class_attrs['__primary_key__'] = pk_fields
|
|
312
|
+
|
|
313
|
+
# Add field annotations from schema properties
|
|
314
|
+
annotations: dict[str, type] = {}
|
|
315
|
+
if object_schema.properties:
|
|
316
|
+
for field_name, field_def in object_schema.properties.items():
|
|
317
|
+
# Map CoreTypes to Python types for annotations
|
|
318
|
+
field_type = self._core_type_to_python_type(getattr(field_def, 'type', 'string'))
|
|
319
|
+
annotations[field_name] = field_type
|
|
320
|
+
|
|
321
|
+
class_attrs['__annotations__'] = annotations
|
|
322
|
+
|
|
323
|
+
# Create the model class dynamically
|
|
324
|
+
model_class = type(model_name, (ExternalModel,), class_attrs)
|
|
325
|
+
|
|
326
|
+
return cast(type[ExternalModel], model_class)
|
|
327
|
+
|
|
328
|
+
@staticmethod
|
|
329
|
+
def _core_type_to_python_type(core_type: str) -> type:
|
|
330
|
+
"""
|
|
331
|
+
Convert CoreType string to Python type for annotations.
|
|
332
|
+
|
|
333
|
+
Args:
|
|
334
|
+
core_type: CoreType value (e.g., 'string', 'integer')
|
|
335
|
+
|
|
336
|
+
Returns:
|
|
337
|
+
type: Corresponding Python type
|
|
338
|
+
"""
|
|
339
|
+
type_mapping = {
|
|
340
|
+
'string': str,
|
|
341
|
+
'integer': int,
|
|
342
|
+
'number': float,
|
|
343
|
+
'boolean': bool,
|
|
344
|
+
'date': str, # Will be string representation
|
|
345
|
+
'datetime': str, # Will be string representation
|
|
346
|
+
'binary': bytes,
|
|
347
|
+
'array': list,
|
|
348
|
+
'dictionary': dict,
|
|
349
|
+
}
|
|
350
|
+
return type_mapping.get(core_type, str)
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
from _typeshed import Incomplete
|
|
2
|
+
from amsdal.services.external_connections import ExternalConnectionManager as ExternalConnectionManager
|
|
3
|
+
from amsdal_models.classes.external_model import ExternalModel
|
|
4
|
+
from amsdal_utils.schemas.schema import ObjectSchema as ObjectSchema
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
class ExternalModelGenerator:
|
|
8
|
+
"""
|
|
9
|
+
Service for generating ExternalModel classes from external connections.
|
|
10
|
+
|
|
11
|
+
This service introspects external database schemas and generates
|
|
12
|
+
corresponding ExternalModel classes that can be used immediately
|
|
13
|
+
for querying the external data.
|
|
14
|
+
|
|
15
|
+
Features:
|
|
16
|
+
- Automatic schema introspection
|
|
17
|
+
- Type mapping (SQL types -> Python types)
|
|
18
|
+
- Primary key detection
|
|
19
|
+
- In-memory model class generation
|
|
20
|
+
- No lakehouse schema creation
|
|
21
|
+
|
|
22
|
+
Example usage:
|
|
23
|
+
# Generate models for a single table
|
|
24
|
+
generator = ExternalModelGenerator()
|
|
25
|
+
User = generator.generate_model('external_db', 'users')
|
|
26
|
+
|
|
27
|
+
# Now use the generated model
|
|
28
|
+
users = User.objects.filter(active=True).execute()
|
|
29
|
+
|
|
30
|
+
# Generate models for all tables
|
|
31
|
+
models = generator.generate_models_for_connection('external_db')
|
|
32
|
+
User = models['User']
|
|
33
|
+
Post = models['Post']
|
|
34
|
+
"""
|
|
35
|
+
_connection_manager: Incomplete
|
|
36
|
+
_schema_converter: Incomplete
|
|
37
|
+
def __init__(self) -> None: ...
|
|
38
|
+
def generate_model(self, connection_name: str, table_name: str, model_name: str | None = None) -> type[ExternalModel]:
|
|
39
|
+
"""
|
|
40
|
+
Generate an ExternalModel class for a specific table.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
connection_name: Name of the external connection
|
|
44
|
+
table_name: Name of the table to generate model for
|
|
45
|
+
model_name: Optional custom model name (defaults to classified table name)
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
type[ExternalModel]: Generated model class ready to use
|
|
49
|
+
|
|
50
|
+
Raises:
|
|
51
|
+
ValueError: If connection doesn't support schema introspection
|
|
52
|
+
ConnectionError: If connection is not available
|
|
53
|
+
RuntimeError: If model generation fails
|
|
54
|
+
|
|
55
|
+
Example:
|
|
56
|
+
generator = ExternalModelGenerator()
|
|
57
|
+
User = generator.generate_model('external_db', 'users')
|
|
58
|
+
|
|
59
|
+
# Query using the generated model
|
|
60
|
+
active_users = User.objects.filter(active=True).execute()
|
|
61
|
+
"""
|
|
62
|
+
def generate_models_for_connection(self, connection_name: str, table_names: list[str] | None = None) -> dict[str, type[ExternalModel]]:
|
|
63
|
+
"""
|
|
64
|
+
Generate ExternalModel classes for all tables in a connection.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
connection_name: Name of the external connection
|
|
68
|
+
table_names: Optional list of specific tables to generate models for.
|
|
69
|
+
If None, generates models for all tables.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
dict[str, type[ExternalModel]]: Dictionary mapping model names to model classes
|
|
73
|
+
|
|
74
|
+
Raises:
|
|
75
|
+
ValueError: If connection doesn't support schema introspection
|
|
76
|
+
ConnectionError: If connection is not available
|
|
77
|
+
|
|
78
|
+
Example:
|
|
79
|
+
generator = ExternalModelGenerator()
|
|
80
|
+
models = generator.generate_models_for_connection('external_db')
|
|
81
|
+
|
|
82
|
+
# Access generated models
|
|
83
|
+
User = models['User']
|
|
84
|
+
Post = models['Post']
|
|
85
|
+
Comment = models['Comment']
|
|
86
|
+
|
|
87
|
+
# Or generate only specific tables
|
|
88
|
+
models = generator.generate_models_for_connection(
|
|
89
|
+
'external_db',
|
|
90
|
+
table_names=['users', 'posts']
|
|
91
|
+
)
|
|
92
|
+
"""
|
|
93
|
+
def _convert_schema(self, connection: Any, table_name: str, table_schema: list[dict[str, Any]], connection_name: str) -> ObjectSchema:
|
|
94
|
+
"""
|
|
95
|
+
Convert raw table schema to ObjectSchema based on connection type.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
connection: The connection object
|
|
99
|
+
table_name: Name of the table
|
|
100
|
+
table_schema: Raw schema data from connection
|
|
101
|
+
connection_name: Name of the connection
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
ObjectSchema: Converted schema
|
|
105
|
+
"""
|
|
106
|
+
def _normalize_schema_format(self, table_schema: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
107
|
+
"""
|
|
108
|
+
Normalize various schema formats to generic format.
|
|
109
|
+
|
|
110
|
+
Converts various schema formats to the format expected by generic_schema_to_object_schema:
|
|
111
|
+
{'name': str, 'type': str, 'nullable': bool, 'primary_key': bool, 'default': Any}
|
|
112
|
+
"""
|
|
113
|
+
def _create_model_class(self, object_schema: ObjectSchema, custom_name: str | None = None) -> type[ExternalModel]:
|
|
114
|
+
"""
|
|
115
|
+
Create an ExternalModel class from ObjectSchema.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
object_schema: The schema to create model from
|
|
119
|
+
custom_name: Optional custom model name
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
type[ExternalModel]: Generated model class
|
|
123
|
+
"""
|
|
124
|
+
@staticmethod
|
|
125
|
+
def _core_type_to_python_type(core_type: str) -> type:
|
|
126
|
+
"""
|
|
127
|
+
Convert CoreType string to Python type for annotations.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
core_type: CoreType value (e.g., 'string', 'integer')
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
type: Corresponding Python type
|
|
134
|
+
"""
|
|
Binary file
|