0
# Autogeneration
1
2
Automatic migration generation by comparing database schema with SQLAlchemy model definitions. The autogeneration system includes customizable comparison and rendering systems for generating migration scripts.
3
4
## Core Imports
5
6
```python
7
from alembic.autogenerate import compare_metadata, produce_migrations, render_python_code
8
from alembic.autogenerate.api import RevisionContext, AutogenContext, _render_migration_diffs
9
from alembic.autogenerate.compare import _produce_net_changes, comparators
10
from alembic.autogenerate.render import render_op_text, renderers
11
```
12
13
## Capabilities
14
15
### Metadata Comparison
16
17
Compare target metadata against database schema to identify differences.
18
19
```python { .api }
20
def compare_metadata(context, metadata):
21
"""
22
Compare metadata against database and return differences.
23
24
Args:
25
context (MigrationContext): Migration context with database connection
26
metadata (MetaData): SQLAlchemy metadata to compare
27
28
Returns:
29
list: List of operation objects representing differences
30
"""
31
```
32
33
**Usage Example**:
34
```python
35
from alembic.autogenerate import compare_metadata
36
from alembic.runtime.migration import MigrationContext
37
from sqlalchemy import create_engine
38
39
engine = create_engine('postgresql://user:pass@localhost/db')
40
with engine.connect() as conn:
41
context = MigrationContext.configure(conn)
42
diffs = compare_metadata(context, target_metadata)
43
for diff in diffs:
44
print(diff)
45
```
46
47
### Migration Production
48
49
Generate migration operations from metadata comparison.
50
51
```python { .api }
52
def produce_migrations(context, metadata):
53
"""
54
Produce migration operations from metadata comparison.
55
56
Args:
57
context (MigrationContext): Migration context
58
metadata (MetaData): Target metadata
59
60
Returns:
61
UpgradeOps: Container of upgrade operations
62
"""
63
```
64
65
**Usage Example**:
66
```python
67
from alembic.autogenerate import produce_migrations
68
69
operations = produce_migrations(context, target_metadata)
70
for op in operations.ops:
71
print(f"Operation: {op}")
72
```
73
74
### Python Code Rendering
75
76
Render migration operations as Python code for migration scripts.
77
78
```python { .api }
79
def render_python_code(up_or_down_op, sqlalchemy_module_prefix='sa.', alembic_module_prefix='op.', render_as_batch=False, imports=None, render_item=None):
80
"""
81
Render operations as Python code.
82
83
Args:
84
up_or_down_op (OperationContainer): Operations to render
85
sqlalchemy_module_prefix (str): Prefix for SQLAlchemy imports
86
alembic_module_prefix (str): Prefix for Alembic operations
87
render_as_batch (bool): Render as batch operations
88
imports (set): Set to collect required imports
89
render_item (callable): Custom rendering function
90
91
Returns:
92
str: Python code representation
93
"""
94
```
95
96
**Usage Example**:
97
```python
98
from alembic.autogenerate import render_python_code
99
100
# Render upgrade operations
101
imports = set()
102
code = render_python_code(
103
operations,
104
sqlalchemy_module_prefix='sa.',
105
alembic_module_prefix='op.',
106
imports=imports
107
)
108
print("Required imports:", imports)
109
print("Migration code:")
110
print(code)
111
```
112
113
### Internal Rendering Functions
114
115
Internal functions for advanced migration rendering and processing.
116
117
```python { .api }
118
def _render_migration_diffs(autogen_context, template_args):
119
"""
120
Render migration differences for template generation.
121
122
Args:
123
autogen_context (AutogenContext): Autogeneration context
124
template_args (dict): Template arguments
125
126
Returns:
127
str: Rendered migration code
128
"""
129
130
def _produce_net_changes(autogen_context, metadata):
131
"""
132
Produce net changes from metadata comparison.
133
134
Args:
135
autogen_context (AutogenContext): Autogeneration context
136
metadata (MetaData): Target metadata
137
138
Returns:
139
UpgradeOps: Net change operations
140
"""
141
142
def render_op_text(autogen_context, op):
143
"""
144
Render operation as text representation.
145
146
Args:
147
autogen_context (AutogenContext): Autogeneration context
148
op (MigrateOperation): Operation to render
149
150
Returns:
151
str: Text representation of operation
152
"""
153
```
154
155
### Revision Context
156
157
Context manager for revision generation with autogeneration support.
158
159
```python { .api }
160
class RevisionContext:
161
def __init__(self, config, script_directory, command_args, process_revision_directives=None):
162
"""
163
Context for revision generation.
164
165
Args:
166
config (Config): Alembic configuration
167
script_directory (ScriptDirectory): Script directory
168
command_args (dict): Command arguments
169
process_revision_directives (callable): Custom directive processing
170
"""
171
172
def run_autogenerate(self, rev_id, context):
173
"""
174
Run autogeneration process.
175
176
Args:
177
rev_id (str): Revision identifier
178
context (MigrationContext): Migration context
179
180
Returns:
181
Script: Generated script object
182
"""
183
184
def run_no_autogenerate(self, rev_id, context):
185
"""
186
Create empty revision without autogeneration.
187
188
Args:
189
rev_id (str): Revision identifier
190
context (MigrationContext): Migration context
191
192
Returns:
193
Script: Generated script object
194
"""
195
```
196
197
### Autogeneration Context
198
199
Context object containing autogeneration configuration and state.
200
201
```python { .api }
202
class AutogenContext:
203
def __init__(self, migration_context, metadata=None, opts=None, autogenerate=True):
204
"""
205
Context for autogeneration operations.
206
207
Args:
208
migration_context (MigrationContext): Migration context
209
metadata (MetaData): Target metadata
210
opts (dict): Configuration options
211
autogenerate (bool): Enable autogeneration
212
"""
213
214
# Properties and methods available on AutogenContext
215
migration_context: MigrationContext
216
metadata: MetaData
217
connection: Connection
218
dialect: Dialect
219
imports: Set[str]
220
```
221
222
## Comparison Customization
223
224
### Custom Comparators
225
226
Register custom comparison functions for specific object types.
227
228
```python { .api }
229
from alembic.autogenerate import comparators
230
231
@comparators.dispatch_for("schema")
232
def compare_schema(autogen_context, upgrade_ops, schemas):
233
"""
234
Custom schema comparison.
235
236
Args:
237
autogen_context (AutogenContext): Autogeneration context
238
upgrade_ops (UpgradeOps): Container for upgrade operations
239
schemas (set): Set of schema names
240
"""
241
242
@comparators.dispatch_for("table")
243
def compare_table(autogen_context, upgrade_ops, schema, table_name, conn_table, metadata_table):
244
"""
245
Custom table comparison.
246
247
Args:
248
autogen_context (AutogenContext): Autogeneration context
249
upgrade_ops (UpgradeOps): Container for upgrade operations
250
schema (str): Schema name
251
table_name (str): Table name
252
conn_table (Table): Database table reflection
253
metadata_table (Table): Metadata table definition
254
"""
255
```
256
257
**Example Usage**:
258
```python
259
@comparators.dispatch_for("table")
260
def ignore_temp_tables(autogen_context, upgrade_ops, schema, table_name, conn_table, metadata_table):
261
"""Ignore temporary tables during comparison."""
262
if table_name.startswith('temp_'):
263
return
264
# Continue with default comparison
265
return comparators.dispatch("table")(autogen_context, upgrade_ops, schema, table_name, conn_table, metadata_table)
266
```
267
268
### Type Comparison
269
270
Customize how column types are compared during autogeneration.
271
272
```python
273
def compare_type(context, inspected_column, metadata_column, inspected_type, metadata_type):
274
"""
275
Compare column types for differences.
276
277
Args:
278
context (MigrationContext): Migration context
279
inspected_column (dict): Database column information
280
metadata_column (Column): Metadata column
281
inspected_type (TypeEngine): Database column type
282
metadata_type (TypeEngine): Metadata column type
283
284
Returns:
285
bool: True if types are different
286
"""
287
# Custom type comparison logic
288
if isinstance(metadata_type, sa.String) and isinstance(inspected_type, sa.Text):
289
return False # Consider String and Text as equivalent
290
return None # Use default comparison
291
292
# Configure in context.configure()
293
context.configure(
294
connection=connection,
295
target_metadata=target_metadata,
296
compare_type=compare_type
297
)
298
```
299
300
### Server Default Comparison
301
302
Customize server default comparison logic.
303
304
```python
305
def compare_server_default(context, inspected_column, metadata_column, inspected_default, metadata_default, rendered_metadata_default):
306
"""
307
Compare server defaults for differences.
308
309
Args:
310
context (MigrationContext): Migration context
311
inspected_column (dict): Database column information
312
metadata_column (Column): Metadata column
313
inspected_default (str): Database default value
314
metadata_default: Metadata default value
315
rendered_metadata_default (str): Rendered metadata default
316
317
Returns:
318
bool: True if defaults are different
319
"""
320
# Custom server default comparison
321
if inspected_default == "''" and metadata_default is None:
322
return False # Empty string equals NULL
323
return None # Use default comparison
324
325
context.configure(
326
connection=connection,
327
target_metadata=target_metadata,
328
compare_server_default=compare_server_default
329
)
330
```
331
332
## Rendering Customization
333
334
### Custom Renderers
335
336
Register custom rendering functions for specific operations.
337
338
```python { .api }
339
from alembic.autogenerate import renderers
340
341
@renderers.dispatch_for(CreateTableOp)
342
def render_create_table(autogen_context, op):
343
"""
344
Custom rendering for CREATE TABLE operations.
345
346
Args:
347
autogen_context (AutogenContext): Autogeneration context
348
op (CreateTableOp): Create table operation
349
350
Returns:
351
str: Python code for the operation
352
"""
353
return f"op.create_table({op.table_name!r}, ...)"
354
```
355
356
### Include/Exclude Filtering
357
358
Filter objects during autogeneration.
359
360
```python
361
def include_object(object, name, type_, reflected, compare_to):
362
"""
363
Determine whether to include an object in autogeneration.
364
365
Args:
366
object: Database object
367
name (str): Object name
368
type_ (str): Object type ('table', 'column', 'index', etc.)
369
reflected (bool): True if object was reflected from database
370
compare_to: Corresponding metadata object
371
372
Returns:
373
bool: True to include object in comparison
374
"""
375
# Skip temporary tables
376
if type_ == "table" and name.startswith("temp_"):
377
return False
378
379
# Skip certain indexes
380
if type_ == "index" and name.startswith("_"):
381
return False
382
383
return True
384
385
def include_name(name, type_, parent_names):
386
"""
387
Filter object names during reflection.
388
389
Args:
390
name (str): Object name
391
type_ (str): Object type
392
parent_names (dict): Parent object names
393
394
Returns:
395
bool: True to include object
396
"""
397
# Skip system schemas
398
if type_ == "schema" and name.startswith("information_"):
399
return False
400
return True
401
402
# Configure filtering
403
context.configure(
404
connection=connection,
405
target_metadata=target_metadata,
406
include_object=include_object,
407
include_name=include_name
408
)
409
```
410
411
## Advanced Features
412
413
### Process Revision Directives
414
415
Customize the revision generation process.
416
417
```python
418
def process_revision_directives(context, revision, directives):
419
"""
420
Process and modify revision directives.
421
422
Args:
423
context (MigrationContext): Migration context
424
revision (tuple): Revision information
425
directives (list): List of MigrationScript directives
426
"""
427
# Skip empty migrations
428
script = directives[0]
429
if script.upgrade_ops.is_empty():
430
directives[:] = []
431
print("Skipping empty migration")
432
return
433
434
# Add custom header comment
435
script.upgrade_ops.ops.insert(0,
436
ExecuteSQLOp("-- Auto-generated migration"))
437
438
# Use in context configuration
439
context.configure(
440
connection=connection,
441
target_metadata=target_metadata,
442
process_revision_directives=process_revision_directives
443
)
444
```
445
446
### Template Arguments
447
448
Pass custom variables to migration templates.
449
450
```python
451
def process_revision_directives(context, revision, directives):
452
"""Add custom template variables."""
453
script = directives[0]
454
script.template_args = {
455
'author': 'AutoGen System',
456
'created_at': datetime.now().isoformat()
457
}
458
459
# In migration template
460
"""${message}
461
462
Revision ID: ${up_revision}
463
Revises: ${down_revision | comma,n}
464
Create Date: ${create_date}
465
Author: ${author}
466
Created At: ${created_at}
467
"""
468
```
469
470
### Batch Rendering
471
472
Render operations suitable for SQLite batch mode.
473
474
```python
475
# Configure batch rendering
476
context.configure(
477
connection=connection,
478
target_metadata=target_metadata,
479
render_as_batch=True # Use batch operations
480
)
481
482
# Or in rendering
483
code = render_python_code(
484
operations,
485
render_as_batch=True
486
)
487
```
488
489
## Integration Examples
490
491
### Flask-SQLAlchemy Integration
492
493
```python
494
from flask import current_app
495
from alembic import context
496
from flask_sqlalchemy import SQLAlchemy
497
498
def get_metadata():
499
"""Get metadata from Flask-SQLAlchemy."""
500
with current_app.app_context():
501
return current_app.extensions['sqlalchemy'].db.metadata
502
503
def run_migrations_online():
504
"""Run migrations with Flask app context."""
505
connectable = engine_from_config(
506
config.get_section(config.config_ini_section),
507
prefix='sqlalchemy.',
508
poolclass=pool.NullPool,
509
)
510
511
with connectable.connect() as connection:
512
context.configure(
513
connection=connection,
514
target_metadata=get_metadata(),
515
include_object=include_object,
516
compare_type=True,
517
compare_server_default=True
518
)
519
520
with context.begin_transaction():
521
context.run_migrations()
522
```
523
524
### Django Integration
525
526
```python
527
def run_autogenerate_with_django():
528
"""Run autogeneration with Django models."""
529
import django
530
from django.conf import settings
531
532
django.setup()
533
534
# Convert Django models to SQLAlchemy metadata
535
metadata = convert_django_models_to_sqlalchemy()
536
537
engine = create_engine(get_database_url())
538
with engine.connect() as connection:
539
context = MigrationContext.configure(
540
connection,
541
opts={
542
'target_metadata': metadata,
543
'compare_type': True,
544
'include_object': include_object
545
}
546
)
547
548
diffs = compare_metadata(context, metadata)
549
return diffs
550
```
551
552
## Types
553
554
```python { .api }
555
# Autogeneration context types
556
class AutogenContext:
557
migration_context: MigrationContext
558
metadata: Optional[MetaData]
559
connection: Connection
560
dialect: Dialect
561
imports: Set[str]
562
563
class RevisionContext:
564
config: Config
565
script_directory: ScriptDirectory
566
command_args: Dict[str, Any]
567
568
# Operation containers
569
class UpgradeOps:
570
ops: List[MigrateOperation]
571
572
class DowngradeOps:
573
ops: List[MigrateOperation]
574
575
# Comparison function types
576
CompareTypeFunc = Callable[[MigrationContext, Column, Column, TypeEngine, TypeEngine], Optional[bool]]
577
CompareServerDefaultFunc = Callable[[MigrationContext, Column, Column, Any, Any, str], Optional[bool]]
578
IncludeObjectFunc = Callable[[Any, str, str, bool, Any], bool]
579
ProcessRevisionDirectivesFunc = Callable[[MigrationContext, Tuple, List], None]
580
```