SHARE
TWEET

Untitled

rowanh Jun 19th, 2019 75 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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))
  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:
  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()
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
Not a member of Pastebin yet?
Sign Up, it unlocks many cool features!
 
Top