Files
vidconf/backend/alembic/versions/d87681e12784_conference_invitees_and_avatars.py

77 lines
3.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""conference invitees and avatars
Реализует раздел «Модель данных» ADR-003
(`docs/architecture/adr/003-conference-invitees.md`):
- новая таблица `conference_invitees` — приглашённые НА КОНФЕРЕНЦИЮ
(зарегистрированный `user_id` ИЛИ внешний `email`, ровно одна identity);
организатор в таблице не хранится (выводится из `conferences.owner_id`);
- `users.avatar_path` — путь к загруженному аватару,
`NULL` — заглушка с инициалами на фронте.
Revision ID: d87681e12784
Revises: 504791847d4f
Create Date: 2026-07-19 12:03:06.294730
"""
from typing import Sequence, Union
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision: str = 'd87681e12784'
down_revision: Union[str, Sequence[str], None] = '504791847d4f'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
def upgrade() -> None:
"""Upgrade schema."""
op.create_table(
'conference_invitees',
sa.Column('id', sa.UUID(), server_default=sa.text('gen_random_uuid()'), nullable=False),
sa.Column('conference_id', sa.UUID(), nullable=False),
sa.Column('user_id', sa.UUID(), nullable=True),
sa.Column('email', sa.String(length=255), nullable=True),
sa.Column(
'created_at', sa.DateTime(timezone=True), server_default=sa.text('now()'),
nullable=False,
),
sa.CheckConstraint(
'(user_id IS NOT NULL)::int + (email IS NOT NULL)::int = 1',
name='ck_conference_invitees_single_identity',
),
sa.ForeignKeyConstraint(['conference_id'], ['conferences.id'], ondelete='CASCADE'),
sa.ForeignKeyConstraint(['user_id'], ['users.id'], ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id'),
)
# Частичные уникальные индексы (ADR-003, п.1): дубль по зарегистрированному
# пользователю или по email (регистронезависимо — `lower(email)`, email
# приложение хранит уже в lower-case, индекс — доп. страховка).
op.create_index(
'uq_conference_invitees_user',
'conference_invitees',
['conference_id', 'user_id'],
unique=True,
postgresql_where=sa.text('user_id IS NOT NULL'),
)
op.execute(
"""
CREATE UNIQUE INDEX uq_conference_invitees_email
ON conference_invitees (conference_id, lower(email))
WHERE email IS NOT NULL
"""
)
op.add_column('users', sa.Column('avatar_path', sa.String(length=512), nullable=True))
def downgrade() -> None:
"""Downgrade schema."""
op.drop_column('users', 'avatar_path')
op.execute('DROP INDEX IF EXISTS uq_conference_invitees_email')
op.drop_index('uq_conference_invitees_user', table_name='conference_invitees')
op.drop_table('conference_invitees')