View difference between Paste ID: jCvFwdCe and 86SyDy7K
SHOW: | | - or go back to the newest paste.
1
import subprocess
2
import re
3
4
5
classes = ['SignupInfo']
6
7
#
8
# Get the relevant files and Django app names for the classes we need to augment
9
#
10
class_to_file_map = {}
11
app_to_classes_map = {}
12
for clazz in classes:
13
    grep_str = subprocess.check_output('grep -r "class ' + clazz + '" **/models.py', shell=True)
14
    matches = re.search('((.*)/models\.py).*', grep_str)
15
16
    app_path = matches.group(2)
17
    django_app = app_path.replace('/', '.')
18
19
    class_to_file_map[clazz] = matches.group(1)
20
21
    if django_app not in app_to_classes_map:
22
        app_to_classes_map[django_app] = []
23
    app_to_classes_map[django_app].append(clazz)
24
25
#
26
# Edit the class definitions to no longer have "db_index=False"
27
#
28
for clazz, file in class_to_file_map.items():
29
    f = open(file, 'r')
30
31
    # Read the lines, then edit the correct one
32
    lines = f.readlines()
33
    for i in range(len(lines)):
34
        if lines[i].find('class {}'.format(clazz)) != -1:
35
            while True:
36
                i += 1
37
                if lines[i].find('organization =') != -1:
38
                    # Funky regex here to capture that db_index may be the first or last arg, which changes
39
                    # the position of the comma that we need to delete.
40
                    db_index_str = re.search('(db_index=False[,]?[\s]?|[,]?[\s]?db_index=False)', lines[i]).group(1)
41
                    lines[i] = lines[i].replace(db_index_str, '')
42
                    break
43
            break
44
    f.close()
45
46
    # Write the editted lines back to the file
47
    f = open(file, 'w+')
48
    f.writelines(lines)
49
    f.close()
50
51
#
52
# Have Django make the migrations for adding the indexes,
53
# then have Django spit out the SQL for those migrations,
54
# then yank the relevant index-creation SQL out of that and augment it to make the index concurrently.
55
#
56
app_to_index_creation_sql = {}
57
django_migration_files_created = []
58
59
django_migs_output = subprocess.check_output('python manage.py makemigrations', shell=True)
60
for line in django_migs_output.split('\n'):
61
    mig_number_match = re.search('([0-9]{4})_', line)
62
    if not mig_number_match:
63
        continue
64
    mig_number = mig_number_match.group(1)
65
    mig_app_name = re.search('\s([\S]+)/migrations', line).group(1)
66
67
    django_migration_file_created = re.search('\s([\S]+\.py)', line).group(1)
68
    django_migration_files_created.append(django_migration_file_created)
69
70
    mig_sql = subprocess.check_output('python manage.py sqlmigrate {} {}'.format(mig_app_name, mig_number), shell=True)
71
    index_creation_sql_lines = re.findall('(CREATE INDEX [^;]+;)', mig_sql)
72
73
    for i in range(len(index_creation_sql_lines)):
74
        sql_line = index_creation_sql_lines[i]
75
        sql_line = sql_line.replace('INDEX', 'INDEX CONCURRENTLY')
76
        index_creation_sql_lines[i] = sql_line
77
78
    app_to_index_creation_sql[mig_app_name] = index_creation_sql_lines
79
80
#
81
# These migration files are no longer created, we're gonna make new ones from scratch. NUKE TIME
82
#
83
for mig_file in django_migration_files_created:
84
    subprocess.check_output('rm {}'.format(mig_file), shell=True)
85
86
#
87
# Old migration files: Who are you?
88
# New migration files: I'm you but better
89
#
90
# Time to make new migration files with the SQL for making indexes concurrently
91
#
92
for django_app in app_to_classes_map.keys():
93
    makemig_output = subprocess.check_output('python manage.py makemigrations --empty ' + django_app, shell=True)
94
    new_migration_file = re.search('\s([\S]+/migrations.*\.py)', makemig_output).group(1)
95
96
    f = open(new_migration_file, 'r')
97
98
    lines = f.readlines()
99
    for i in range(len(lines)):
100
        line = lines[i]
101
        if line.startswith('class'):
102
            break
103
104
    lines.insert(i, '\n')
105
    for j, sql_line in enumerate(app_to_index_creation_sql[django_app]):
106
        lines.insert(i, 'INDEX_CREATION_SQL_{} = \'{}\'\n'.format(j, sql_line))
107
    lines.insert(i, 'from django.conf import settings\n')
108
109
    lines.append('    if not settings.INITIAL_DATABASE_SETUP:\n')
110
    for j, __ in enumerate(app_to_index_creation_sql[django_app]):
111-
        lines.append('        operations.append(migrations.RunSQL(INDEX_CREATION_SQL_{}), migrations.RunSQL.noop)\n'.format(j))
111+
        lines.append('        operations.append(migrations.RunSQL(INDEX_CREATION_SQL_{}, migrations.RunSQL.noop))\n'.format(j))
112
    lines.append('    atomic = False')
113
114
    f.close()
115
116
    f = open(new_migration_file, 'w+')
117
    f.writelines(lines)
118
    f.close()
119
120
#
121
# Take a quick trip back in time to erase Django's memory of ever having db_index=False for the associated classes
122
#
123
for django_app, classes in app_to_classes_map.items():
124
    for clazz in classes:
125
126
        # We'll do this in a try/catch, since there's a chance that there have been
127
        # no AddField/AlterField operations on Organization for this class
128
        try:
129
            grep_output = subprocess.check_output('grep -i "\(addfield\|alterfield\|createmodel\)" {}/migrations/* -A 10 | grep -i organization -B 10 | grep -i {}'
130
                                                  .format(django_app.replace('.', '/'),
131
                                                          clazz),
132
                                                  shell=True)
133
        except:
134
            continue
135
136
        files_to_alter = []
137
        for line in grep_output.split('\n'):
138
            if line:
139
                files_to_alter.append(re.search('([\S]+\.py)', line).group(1))
140
141-
        for file in files_to_alter:
141+
        for file in set(files_to_alter):
142
            f = open(file, 'r')
143
144
            lines = f.readlines()
145
146
            upcoming_edit_line = False
147
            for i in range(len(lines)):
148
                line = lines[i]
149
				found_index = max(line.find(clazz.lower()), line.find(clazz))
150
                if found_index != -1 and line[found_index - 1] == "'":
151
                    upcoming_edit_line = True
152
153
                if upcoming_edit_line and line.find('db_index') != -1:
154
                    # Same funky regex here as before to catch the fact that the comma we need
155
                    # to delete may be before or after the arg but NOT BOTH
156
                    db_index_str = re.search('(db_index=False[,]?[\s]?|[,]?[\s]?db_index=False)', line).group(1)
157
                    lines[i] = line.replace(db_index_str, '')
158
                    upcoming_edit_line = False
159
160
            f.close()
161
162
            f = open(file, 'w+')
163
            f.writelines(lines)
164
            f.close()