0
# Migration Operations
1
2
Schema modification operations available in migration scripts through the `alembic.op` module. These operations provide a high-level interface for database schema changes including tables, columns, constraints, indexes, and data manipulation.
3
4
## Core Imports
5
6
```python
7
from alembic import op
8
import sqlalchemy as sa
9
```
10
11
## Capabilities
12
13
### Table Operations
14
15
Create, drop, and modify database tables.
16
17
```python { .api }
18
def create_table(table_name, *columns, if_not_exists=None, **kw):
19
"""
20
Create a new table.
21
22
Args:
23
table_name (str): Name of the table to create
24
*columns: SQLAlchemy Column objects
25
if_not_exists (bool): Add IF NOT EXISTS clause (v1.16.0+)
26
**kw: Additional table options (schema, mysql_engine, etc.)
27
28
Returns:
29
Table: SQLAlchemy Table object
30
"""
31
32
def drop_table(table_name, if_exists=None, **kw):
33
"""
34
Drop an existing table.
35
36
Args:
37
table_name (str): Name of the table to drop
38
if_exists (bool): Add IF EXISTS clause (v1.16.0+)
39
**kw: Additional options (schema, etc.)
40
41
Returns:
42
None
43
"""
44
45
def rename_table(old_table_name, new_table_name, schema=None):
46
"""
47
Rename a table.
48
49
Args:
50
old_table_name (str): Current table name
51
new_table_name (str): New table name
52
schema (str): Schema name if applicable
53
54
Returns:
55
None
56
"""
57
```
58
59
**Usage Examples**:
60
```python
61
# Create table with columns
62
op.create_table('user',
63
sa.Column('id', sa.Integer, primary_key=True),
64
sa.Column('name', sa.String(50), nullable=False),
65
sa.Column('email', sa.String(120), unique=True),
66
sa.Column('created_at', sa.DateTime, default=sa.func.now())
67
)
68
69
# Drop table
70
op.drop_table('old_table')
71
72
# Rename table
73
op.rename_table('user', 'users')
74
```
75
76
### Column Operations
77
78
Add, remove, and modify table columns.
79
80
```python { .api }
81
def add_column(table_name, column, if_not_exists=None, **kw):
82
"""
83
Add a column to an existing table.
84
85
Args:
86
table_name (str): Name of the table
87
column (Column): SQLAlchemy Column object to add
88
if_not_exists (bool): Add IF NOT EXISTS clause (v1.16.0+)
89
**kw: Additional options (schema, etc.)
90
"""
91
92
def drop_column(table_name, column_name, **kw):
93
"""
94
Drop a column from a table.
95
96
Args:
97
table_name (str): Name of the table
98
column_name (str): Name of the column to drop
99
**kw: Additional options (schema, etc.)
100
"""
101
102
def alter_column(table_name, column_name, nullable=None, comment=False, server_default=False, new_column_name=None, type_=None, **kw):
103
"""
104
Alter properties of an existing column.
105
106
Args:
107
table_name (str): Name of the table
108
column_name (str): Name of the column to alter
109
nullable (bool): Change nullable constraint
110
comment (str|False): Change column comment (False=no change, None=remove)
111
server_default (str|False): Change server default (False=no change, None=remove)
112
new_column_name (str): Rename the column
113
type_ (TypeEngine): Change column type
114
**kw: Additional options (schema, etc.)
115
"""
116
```
117
118
**Usage Examples**:
119
```python
120
# Add column
121
op.add_column('user', sa.Column('phone', sa.String(20)))
122
123
# Drop column
124
op.drop_column('user', 'phone')
125
126
# Alter column - change type and nullable
127
op.alter_column('user', 'email', type_=sa.String(200), nullable=True)
128
129
# Rename column
130
op.alter_column('user', 'name', new_column_name='full_name')
131
132
# Change server default
133
op.alter_column('user', 'created_at', server_default=sa.text('CURRENT_TIMESTAMP'))
134
```
135
136
### Constraint Operations
137
138
Create and drop various types of database constraints.
139
140
```python { .api }
141
def create_primary_key(constraint_name, table_name, columns, schema=None):
142
"""
143
Create a primary key constraint.
144
145
Args:
146
constraint_name (str): Name of the constraint
147
table_name (str): Name of the table
148
columns (list): List of column names
149
schema (str): Schema name if applicable
150
151
Returns:
152
None
153
"""
154
155
def create_foreign_key(constraint_name, source_table, referent_table, local_cols, remote_cols, onupdate=None, ondelete=None, deferrable=None, initially=None, match=None, source_schema=None, referent_schema=None, **dialect_kw):
156
"""
157
Create a foreign key constraint.
158
159
Args:
160
constraint_name (str): Name of the constraint
161
source_table (str): Source table name
162
referent_table (str): Referenced table name
163
local_cols (list): Local column names
164
remote_cols (list): Referenced column names
165
onupdate (str): ON UPDATE action
166
ondelete (str): ON DELETE action
167
deferrable (bool): Whether constraint is deferrable
168
initially (str): Initial constraint state ('DEFERRED' or 'IMMEDIATE')
169
match (str): MATCH clause ('FULL', 'PARTIAL', 'SIMPLE')
170
source_schema (str): Source table schema
171
referent_schema (str): Referenced table schema
172
**dialect_kw: Additional dialect-specific options
173
174
Returns:
175
None
176
"""
177
178
def create_check_constraint(constraint_name, table_name, condition, schema=None, **kw):
179
"""
180
Create a check constraint.
181
182
Args:
183
constraint_name (str): Name of the constraint
184
table_name (str): Name of the table
185
condition (str|ClauseElement): Check condition
186
schema (str): Schema name if applicable
187
**kw: Additional options
188
189
Returns:
190
None
191
"""
192
193
def create_unique_constraint(constraint_name, table_name, columns, schema=None, **kw):
194
"""
195
Create a unique constraint.
196
197
Args:
198
constraint_name (str): Name of the constraint
199
table_name (str): Name of the table
200
columns (list): List of column names
201
schema (str): Schema name if applicable
202
**kw: Additional options
203
204
Returns:
205
Any: Constraint object
206
"""
207
208
def create_exclude_constraint(constraint_name, table_name, *elements, where=None, schema=None, deferrable=None, initially=None, using=None, **kw):
209
"""
210
Create an exclude constraint (PostgreSQL).
211
212
Args:
213
constraint_name (str): Name of the constraint
214
table_name (str): Name of the table
215
*elements: Column/expression and operator pairs
216
where (str): WHERE clause for partial constraint
217
schema (str): Schema name if applicable
218
deferrable (bool): Whether constraint is deferrable
219
initially (str): Initial constraint checking mode
220
using (str): Index method to use
221
**kw: Additional options
222
223
Returns:
224
Optional[Table]: Table object (if created)
225
"""
226
227
def drop_constraint(constraint_name, table_name, type_=None, if_exists=None, schema=None):
228
"""
229
Drop a constraint.
230
231
Args:
232
constraint_name (str): Name of the constraint to drop
233
table_name (str): Name of the table
234
type_ (str): Constraint type ('foreignkey', 'primary', 'unique', 'check')
235
if_exists (bool): Add IF EXISTS clause (v1.16.0+)
236
schema (str): Schema name if applicable
237
238
Returns:
239
None
240
"""
241
```
242
243
**Usage Examples**:
244
```python
245
# Create primary key
246
op.create_primary_key('pk_user', 'user', ['id'])
247
248
# Create foreign key
249
op.create_foreign_key('fk_post_user', 'post', 'user', ['user_id'], ['id'], ondelete='CASCADE')
250
251
# Create unique constraint
252
op.create_unique_constraint('uq_user_email', 'user', ['email'])
253
254
# Create check constraint
255
op.create_check_constraint('ck_user_age', 'user', 'age >= 0')
256
257
# Drop constraint
258
op.drop_constraint('fk_old_constraint', 'table_name', type_='foreignkey')
259
```
260
261
### Index Operations
262
263
Create and drop database indexes for query optimization.
264
265
```python { .api }
266
def create_index(index_name, table_name, columns, schema=None, unique=False, if_not_exists=None, **kw):
267
"""
268
Create an index.
269
270
Args:
271
index_name (str): Name of the index
272
table_name (str): Name of the table
273
columns (list): List of column names or expressions
274
schema (str): Schema name if applicable
275
unique (bool): Whether index should be unique
276
if_not_exists (bool): Add IF NOT EXISTS clause (v1.12.0+)
277
**kw: Database-specific index options
278
279
Returns:
280
None
281
"""
282
283
def drop_index(index_name, table_name=None, schema=None, if_exists=None, **kw):
284
"""
285
Drop an index.
286
287
Args:
288
index_name (str): Name of the index to drop
289
table_name (str): Name of the table (optional, some databases require)
290
schema (str): Schema name if applicable
291
if_exists (bool): Add IF EXISTS clause (v1.12.0+)
292
**kw: Additional options
293
294
Returns:
295
None
296
"""
297
```
298
299
**Usage Examples**:
300
```python
301
# Create simple index
302
op.create_index('ix_user_email', 'user', ['email'])
303
304
# Create unique index
305
op.create_index('ix_user_username', 'user', ['username'], unique=True)
306
307
# Create composite index
308
op.create_index('ix_post_user_date', 'post', ['user_id', 'created_at'])
309
310
# Drop index
311
op.drop_index('ix_old_index', 'user')
312
```
313
314
### Data Operations
315
316
Execute SQL statements and perform bulk data operations.
317
318
```python { .api }
319
def execute(sqltext, execution_options=None):
320
"""
321
Execute arbitrary SQL.
322
323
Args:
324
sqltext (str|ClauseElement): SQL statement to execute
325
execution_options (dict): Execution options
326
"""
327
328
def bulk_insert(table, rows, multiinsert=True):
329
"""
330
Perform bulk insert operation.
331
332
Args:
333
table (Table|str): Target table
334
rows (list): List of dictionaries representing rows
335
multiinsert (bool): Use multi-row INSERT statements
336
"""
337
338
def inline_literal(value):
339
"""
340
Create an inline literal value for SQL generation.
341
342
Args:
343
value: Python value to convert to SQL literal
344
345
Returns:
346
Literal value for inline SQL
347
"""
348
```
349
350
**Usage Examples**:
351
```python
352
# Execute raw SQL
353
op.execute("UPDATE user SET status = 'active' WHERE created_at > '2023-01-01'")
354
355
# Bulk insert data
356
user_table = sa.table('user',
357
sa.column('name'),
358
sa.column('email')
359
)
360
op.bulk_insert(user_table, [
361
{'name': 'John', 'email': 'john@example.com'},
362
{'name': 'Jane', 'email': 'jane@example.com'}
363
])
364
365
# Use inline literal
366
op.execute(f"INSERT INTO config (key, value) VALUES ('version', {op.inline_literal('1.0')})")
367
```
368
369
### Batch Operations
370
371
Special operations for databases with limited ALTER support (particularly SQLite).
372
373
```python { .api }
374
def batch_alter_table(table_name, schema=None, recreate='auto', copy_from=None, table_args=(), table_kwargs=None, reflect_args=(), reflect_kwargs=None, naming_convention=None):
375
"""
376
Context manager for batch table alterations.
377
378
Args:
379
table_name (str): Name of the table to alter
380
schema (str): Schema name if applicable
381
recreate (str): Recreation strategy ('auto', 'always', 'never')
382
copy_from (Table): Source table to copy from
383
table_args: Arguments for new table
384
table_kwargs: Keyword arguments for new table
385
reflect_args: Arguments for table reflection
386
reflect_kwargs: Keyword arguments for table reflection
387
naming_convention (dict): Naming convention for constraints
388
389
Returns:
390
BatchOperations: Context manager for batch operations
391
"""
392
```
393
394
**Usage Examples**:
395
```python
396
# Batch alter table for SQLite compatibility
397
with op.batch_alter_table('user') as batch_op:
398
batch_op.add_column(sa.Column('phone', sa.String(20)))
399
batch_op.alter_column('email', nullable=True)
400
batch_op.create_unique_constraint('uq_user_phone', ['phone'])
401
batch_op.drop_column('old_field')
402
```
403
404
### Table Comments
405
406
Add and remove table-level comments.
407
408
```python { .api }
409
def create_table_comment(table_name, comment, schema=None):
410
"""
411
Create a table comment.
412
413
Args:
414
table_name (str): Name of the table
415
comment (str): Comment text
416
schema (str): Schema name if applicable
417
"""
418
419
def drop_table_comment(table_name, schema=None):
420
"""
421
Drop a table comment.
422
423
Args:
424
table_name (str): Name of the table
425
schema (str): Schema name if applicable
426
"""
427
```
428
429
### Advanced Operations
430
431
Advanced operation management and customization.
432
433
```python { .api }
434
def get_bind():
435
"""
436
Get the current database connection.
437
438
Returns:
439
Connection: SQLAlchemy connection object
440
"""
441
442
def get_context():
443
"""
444
Get the current migration context.
445
446
Returns:
447
MigrationContext: Current migration context
448
"""
449
450
def f(name):
451
"""
452
Mark a name as having a naming convention applied.
453
454
Args:
455
name (str): Name to mark for naming convention processing
456
457
Returns:
458
conv: Naming convention placeholder
459
"""
460
461
def run_async(async_function, *args, **kw_args):
462
"""
463
Run an async function in migration context (v1.11+).
464
465
Args:
466
async_function: Async function to execute
467
*args: Positional arguments
468
**kw_args: Keyword arguments
469
470
Returns:
471
Any: Function result
472
"""
473
474
def invoke(operation):
475
"""
476
Invoke a migration operation programmatically.
477
478
Args:
479
operation: Operation object to invoke
480
481
Returns:
482
Any: Operation result
483
"""
484
485
def implementation_for(op_cls):
486
"""
487
Register an implementation for a custom operation class.
488
489
Args:
490
op_cls: Operation class to register implementation for
491
492
Returns:
493
Callable: Decorator function
494
"""
495
496
def register_operation(name, sourcename=None):
497
"""
498
Register a new custom operation.
499
500
Args:
501
name (str): Name of the operation
502
sourcename (str): Source module name
503
504
Returns:
505
Callable: Decorator function
506
"""
507
508
def invoke(operation):
509
"""
510
Invoke a migration operation.
511
512
Args:
513
operation (MigrateOperation): Operation to invoke
514
"""
515
516
def f(name):
517
"""
518
Create a naming convention reference.
519
520
Args:
521
name (str): Template name
522
523
Returns:
524
str: Formatted name according to naming convention
525
"""
526
527
def implementation_for(op_cls):
528
"""
529
Register operation implementation for custom operations.
530
531
Args:
532
op_cls: Operation class to register implementation for
533
534
Returns:
535
Decorator function for implementation registration
536
"""
537
538
def register_operation(name, operation_class):
539
"""
540
Register a custom operation.
541
542
Args:
543
name (str): Operation name
544
operation_class: Custom operation class
545
"""
546
547
def run_async(coro):
548
"""
549
Run async operation within migration context.
550
551
Args:
552
coro: Coroutine to execute
553
554
Returns:
555
Result of coroutine execution
556
"""
557
```
558
559
## Operation Context
560
561
All operations are executed within a migration context that provides:
562
563
- Database connection management
564
- Transaction handling
565
- SQL dialect-specific behavior
566
- Offline SQL generation mode
567
- Custom operation implementations
568
569
## Database-Specific Considerations
570
571
### PostgreSQL
572
- Supports most operations natively
573
- Concurrent index creation: `postgresql_concurrently=True`
574
- Array and JSONB column types supported
575
576
### MySQL
577
- Limited foreign key support in some versions
578
- Engine-specific options: `mysql_engine='InnoDB'`
579
- Charset and collation options available
580
581
### SQLite
582
- Limited ALTER TABLE support
583
- Use `batch_alter_table()` for complex changes
584
- Foreign key constraints require special handling
585
586
### SQL Server
587
- Schema-qualified names supported
588
- Identity column considerations
589
- Specific index options available
590
591
## Error Handling
592
593
Operations may raise:
594
- `OperationalError`: Database-level errors
595
- `ProgrammingError`: SQL syntax or logic errors
596
- `IntegrityError`: Constraint violations
597
- `CommandError`: Alembic-specific operation errors
598
599
## Types
600
601
```python { .api }
602
# Operation result types
603
class Table:
604
name: str
605
schema: Optional[str]
606
columns: List[Column]
607
608
class BatchOperations:
609
def add_column(self, column): ...
610
def drop_column(self, column_name): ...
611
def alter_column(self, column_name, **kw): ...
612
def create_index(self, index_name, columns, **kw): ...
613
def create_unique_constraint(self, constraint_name, columns): ...
614
def create_foreign_key(self, constraint_name, referent_table, local_cols, remote_cols): ...
615
def drop_constraint(self, constraint_name): ...
616
```