pgutils.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. from django.core.paginator import Paginator
  2. from django.db.migrations.operations import RunSQL
  3. class CreatePartialIndex(RunSQL):
  4. CREATE_SQL = """
  5. CREATE INDEX %(index_name)s ON %(table)s (%(field)s)
  6. WHERE %(condition)s;
  7. """
  8. REMOVE_SQL = """
  9. DROP INDEX %(index_name)s
  10. """
  11. def __init__(self, field, index_name, condition):
  12. self.model, self.field = field.split('.')
  13. self.index_name = index_name
  14. self.condition = condition
  15. @property
  16. def reversible(self):
  17. return True
  18. def state_forwards(self, app_label, state):
  19. pass
  20. def database_forwards(self, app_label, schema_editor, from_state, to_state):
  21. model = from_state.apps.get_model(app_label, self.model)
  22. statement = self.CREATE_SQL % {
  23. 'index_name': self.index_name,
  24. 'table': model._meta.db_table,
  25. 'field': self.field,
  26. 'condition': self.condition,
  27. }
  28. schema_editor.execute(statement)
  29. def database_backwards(self, app_label, schema_editor, from_state, to_state):
  30. schema_editor.execute(self.REMOVE_SQL % {'index_name': self.index_name})
  31. def describe(self):
  32. message = "Create PostgreSQL partial index on field %s in %s for %s"
  33. formats = (self.field, self.model_name, self.values)
  34. return message % formats
  35. def batch_update(queryset, step=50):
  36. """
  37. Util because psycopg2 iterators aren't really memory effective
  38. """
  39. paginator = Paginator(queryset.order_by('pk'), step)
  40. for page_number in paginator.page_range:
  41. for obj in paginator.page(page_number).object_list:
  42. yield obj
  43. def batch_delete(queryset, step=50):
  44. """
  45. Another util cos paginator goes bobbins when you are deleting
  46. """
  47. queryset_exists = True
  48. while queryset_exists:
  49. for obj in queryset[:step]:
  50. yield obj
  51. queryset_exists = queryset.exists()
  52. class CreatePartialCompositeIndex(CreatePartialIndex):
  53. CREATE_SQL = """
  54. CREATE INDEX %(index_name)s ON %(table)s (%(fields)s)
  55. WHERE %(condition)s;
  56. """
  57. REMOVE_SQL = """
  58. DROP INDEX %(index_name)s
  59. """
  60. def __init__(self, model, fields, index_name, condition):
  61. self.model = model
  62. self.fields = fields
  63. self.index_name = index_name
  64. self.condition = condition
  65. def database_forwards(self, app_label, schema_editor, from_state, to_state):
  66. model = from_state.apps.get_model(app_label, self.model)
  67. statement = self.CREATE_SQL % {
  68. 'index_name': self.index_name,
  69. 'table': model._meta.db_table,
  70. 'fields': ', '.join(self.fields),
  71. 'condition': self.condition,
  72. }
  73. schema_editor.execute(statement)
  74. def describe(self):
  75. message = ("Create PostgreSQL partial composite index on fields %s in %s for %s")
  76. formats = (', '.join(self.fields), self.model_name, self.values)
  77. return message % formats