chenzhaoyang
2025-12-17 d3e5a4b7658ece4f845bbc0c4f95acf3fbdf8a61
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
from django.db import migrations, connections
from django.conf import settings
from core.redis import start_job_async_or_sync
from core.models import AsyncMigrationStatus
import logging
 
logger = logging.getLogger(__name__)
migration_name = '0030_project_search_vector_index'
 
# Actual DDL to run
def forward_migration(migration_name, db_alias):
    migration = AsyncMigrationStatus.objects.using(db_alias).create(
        name=migration_name,
        status=AsyncMigrationStatus.STATUS_STARTED,
    )
    logger.debug(f'Start async migration {migration_name}')
    
    # Check database backend and use appropriate SQL
    # PostgreSQL: Use CONCURRENTLY and specific index types (BRIN, GIN, etc.)
    sql = '''
    CREATE INDEX CONCURRENTLY IF NOT EXISTS project_search_vector_idx ON project USING GIN (search_vector);
    '''
    
    with connections[db_alias].cursor() as cursor:
        cursor.execute(sql)
    
    migration.status = AsyncMigrationStatus.STATUS_FINISHED
    migration.save(using=db_alias)
    logger.debug(f'Async migration {migration_name} complete')
 
# Reverse DDL
def reverse_migration(migration_name, db_alias):
    migration = AsyncMigrationStatus.objects.using(db_alias).create(
        name=migration_name,
        status=AsyncMigrationStatus.STATUS_STARTED,
    )
    logger.debug(f'Start async migration rollback {migration_name}')
    
    # Drop index (handle database differences)
    sql = 'DROP INDEX CONCURRENTLY IF EXISTS "project_search_vector_idx";'
    
    with connections[db_alias].cursor() as cursor:
        cursor.execute(sql)
    
    migration.status = AsyncMigrationStatus.STATUS_FINISHED
    migration.save(using=db_alias)
    logger.debug(f'Async migration rollback {migration_name} complete')
 
# Hook into Django migration
def forwards(apps, schema_editor):
    db_alias = schema_editor.connection.alias
    conn = connections[db_alias]
    if conn.vendor == 'postgresql':
        start_job_async_or_sync(forward_migration, migration_name=migration_name, db_alias=db_alias)
    else:
        logger.debug(f'No index to create if is sqllite')
 
def backwards(apps, schema_editor):
    db_alias = schema_editor.connection.alias
    conn = connections[db_alias]
    if conn.vendor == 'postgresql':
        start_job_async_or_sync(reverse_migration, migration_name=migration_name, db_alias=db_alias) 
    else:
        logger.debug(f'No index to drop if is sqllite')
 
class Migration(migrations.Migration):
    atomic = False
    dependencies = [
        ('projects', '0029_project_search_vector_and_more'),
    ]
    operations = [
        migrations.RunPython(forwards, backwards),
    ]