2026072155_implement_commission_and_organisation_285f2f9c840a.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. """implement commission and organisation
  2. Revision ID: 285f2f9c840a
  3. Revises: 4e48e5c634f0
  4. Create Date: 2026-07-21 00:55:59.819037
  5. """
  6. from uuid import uuid4
  7. from alembic import op
  8. import sqlalchemy as sa
  9. from sqlalchemy import table, column, String
  10. from sqlalchemy.dialects import postgresql
  11. # revision identifiers, used by Alembic.
  12. revision = '285f2f9c840a'
  13. down_revision = '4e48e5c634f0'
  14. branch_labels = None
  15. depends_on = None
  16. def upgrade():
  17. bind = op.get_bind()
  18. # ---- explicitly create enum types before referencing them anywhere ----
  19. org_role_enum = postgresql.ENUM(
  20. "ORG_ADMIN", "RESPO_BENEVOLE", "RESPO_COMMISSION", name="org_role", create_type=False
  21. )
  22. org_role_enum.create(bind, checkfirst=True)
  23. global_role_enum = postgresql.ENUM("SUPER_ADMIN", "USER", name="global_role", create_type=False)
  24. global_role_enum.create(bind, checkfirst=True)
  25. # ---- schema: new tables (unchanged from your version) ----
  26. op.create_table('organizations',
  27. sa.Column('id', sa.UUID(as_uuid=False), nullable=False),
  28. sa.Column('name', sa.String(length=128), nullable=False),
  29. sa.PrimaryKeyConstraint('id'),
  30. )
  31. op.create_index(op.f('ix_organizations_name'), 'organizations', ['name'], unique=True)
  32. op.create_table('user_organizations',
  33. sa.Column('user_id', sa.UUID(as_uuid=False), nullable=False),
  34. sa.Column('organization_id', sa.UUID(as_uuid=False), nullable=False),
  35. sa.Column('role', org_role_enum, nullable=False),
  36. sa.ForeignKeyConstraint(['organization_id'], ['organizations.id'], ondelete='CASCADE'),
  37. sa.ForeignKeyConstraint(['user_id'], ['user_model.id'], ondelete='CASCADE'),
  38. sa.PrimaryKeyConstraint('user_id', 'organization_id'),
  39. )
  40. op.create_table('commissions',
  41. sa.Column('id', sa.UUID(as_uuid=False), nullable=False),
  42. sa.Column('project_id', sa.UUID(as_uuid=False), nullable=False),
  43. sa.Column('name', sa.String(length=128), nullable=False),
  44. sa.ForeignKeyConstraint(['project_id'], ['projects.id'], ondelete='CASCADE'),
  45. sa.PrimaryKeyConstraint('id'),
  46. )
  47. op.create_table('volunteer_groups',
  48. sa.Column('id', sa.UUID(as_uuid=False), nullable=False),
  49. sa.Column('project_id', sa.UUID(as_uuid=False), nullable=False),
  50. sa.Column('name', sa.String(length=128), nullable=False),
  51. sa.Column('color', sa.String(length=16), nullable=True),
  52. sa.ForeignKeyConstraint(['project_id'], ['projects.id'], ondelete='CASCADE'),
  53. sa.PrimaryKeyConstraint('id'),
  54. )
  55. op.create_table('association_volunteer_group',
  56. sa.Column('volunteer_id', sa.UUID(as_uuid=False), nullable=False),
  57. sa.Column('group_id', sa.UUID(as_uuid=False), nullable=False),
  58. sa.ForeignKeyConstraint(['group_id'], ['volunteer_groups.id'], ondelete='CASCADE'),
  59. sa.ForeignKeyConstraint(['volunteer_id'], ['volunteers.id'], ondelete='CASCADE'),
  60. sa.PrimaryKeyConstraint('volunteer_id', 'group_id'),
  61. )
  62. op.create_table('commission_members',
  63. sa.Column('commission_id', sa.UUID(as_uuid=False), nullable=False),
  64. sa.Column('user_id', sa.UUID(as_uuid=False), nullable=False),
  65. sa.ForeignKeyConstraint(['commission_id'], ['commissions.id'], ondelete='CASCADE'),
  66. sa.ForeignKeyConstraint(['user_id'], ['user_model.id'], ondelete='CASCADE'),
  67. sa.PrimaryKeyConstraint('commission_id', 'user_id'),
  68. )
  69. # ---- schema: new columns, added NULLABLE first ----
  70. op.add_column('projects', sa.Column('organization_id', sa.UUID(as_uuid=False), nullable=True))
  71. op.add_column('user_model', sa.Column('name', sa.String(length=128), nullable=False, server_default=''))
  72. op.add_column('user_model', sa.Column('phone_number', sa.String(length=24), nullable=True))
  73. op.add_column(
  74. "user_model",
  75. sa.Column("global_role", global_role_enum, nullable=False, server_default="USER"),
  76. )
  77. ## ---- data migration ----
  78. organizations_t = table('organizations', column('id', String), column('name', String))
  79. user_organizations_t = table(
  80. 'user_organizations', column('user_id', String), column('organization_id', String), column('role', String)
  81. )
  82. projects_t = table('projects', column('id', String), column('organization_id', String))
  83. users_t = table('user_model', column('id', String))
  84. default_org_id = str(uuid4())
  85. bind.execute(organizations_t.insert().values(id=default_org_id, name='Default'))
  86. bind.execute(projects_t.update().values(organization_id=default_org_id))
  87. existing_user_ids = [row[0] for row in bind.execute(sa.select(users_t.c.id))]
  88. if existing_user_ids:
  89. bind.execute(
  90. user_organizations_t.insert(),
  91. [
  92. {'user_id': uid, 'organization_id': default_org_id, 'role': 'ORG_ADMIN'}
  93. for uid in existing_user_ids
  94. ],
  95. )
  96. # ---- schema: tighten ----
  97. op.alter_column("projects", "organization_id", nullable=False)
  98. op.create_foreign_key(
  99. "fk_projects_organization_id",
  100. "projects",
  101. "organizations",
  102. ["organization_id"],
  103. ["id"],
  104. ondelete="CASCADE",
  105. )
  106. op.alter_column("user_model", "name", server_default=None)
  107. op.alter_column("user_model", "global_role", server_default=None)
  108. def downgrade():
  109. bind = op.get_bind()
  110. op.drop_column('user_model', 'global_role')
  111. op.drop_column('user_model', 'phone_number')
  112. op.drop_column('user_model', 'name')
  113. op.drop_constraint('fk_projects_organization_id', 'projects', type_='foreignkey')
  114. op.drop_column('projects', 'organization_id')
  115. op.drop_table('commission_members')
  116. op.drop_table('association_volunteer_group')
  117. op.drop_table('volunteer_groups')
  118. op.drop_table('commissions')
  119. op.drop_table('user_organizations')
  120. op.drop_index(op.f('ix_organizations_name'), table_name='organizations')
  121. op.drop_table('organizations')
  122. # drop enum types last, after every column referencing them is gone
  123. postgresql.ENUM(name='global_role').drop(bind, checkfirst=True)
  124. postgresql.ENUM(name='org_role').drop(bind, checkfirst=True)