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() |