POL-127: Add organizations table and championship ownership

- Create organizations table with Alembic migration (3-phase: create table, migrate data, drop old column)
- Add org_id FK on championships linking to organizations
- Refactor all schemas into one-class-per-file packages (auth, championship, organization, participant, registration, user)
- Update CRUD layer with selectinload for organization relationships
- Update frontend types and components to use nested organization object
- Remove phantom Championship fields (subtitle, venue, accent_color) from frontend

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dianaka123
2026-03-01 22:09:10 +03:00
parent 96e02bf64a
commit d4f0a05707
44 changed files with 450 additions and 183 deletions

View File

@@ -1,4 +1,4 @@
"""Seed script — creates test users and one championship.
"""Seed script — creates test users, organization, and championships.
Run from backend/: .venv/Scripts/python seed.py
"""
import asyncio
@@ -7,6 +7,7 @@ from datetime import UTC, datetime, timedelta
from app.database import AsyncSessionLocal
from app.models.championship import Championship
from app.models.organization import Organization
from app.models.user import User
from app.services.auth_service import hash_password
from sqlalchemy import select
@@ -29,6 +30,7 @@ async def seed():
"password": "Org1234",
"role": "organizer",
"status": "approved",
"instagram_handle": "@ekaterina_pole",
},
{
"email": "member@pole.dev",
@@ -36,6 +38,7 @@ async def seed():
"password": "Member1234",
"role": "member",
"status": "approved",
"instagram_handle": "@anna_petrova",
},
{
"email": "pending@pole.dev",
@@ -57,11 +60,11 @@ async def seed():
full_name=ud["full_name"],
role=ud["role"],
status=ud["status"],
instagram_handle=ud.get("instagram_handle"),
)
db.add(user)
print(f" Created user: {ud['email']}")
else:
# Update role/status if needed
user.role = ud["role"]
user.status = ud["status"]
user.hashed_password = hash_password(ud["password"])
@@ -70,6 +73,27 @@ async def seed():
await db.flush()
# ── Organization ──────────────────────────────────────────────────────
organizer = created_users["organizer@pole.dev"]
result = await db.execute(select(Organization).where(Organization.user_id == organizer.id))
org = result.scalar_one_or_none()
if org is None:
org = Organization(
user_id=organizer.id,
name="Pole Studio Minsk",
instagram="@polestudio_minsk",
email="organizer@pole.dev",
city="Minsk",
verified=True,
status="active",
)
db.add(org)
print(f" Created organization: {org.name}")
else:
print(f" Organization already exists: {org.name}")
await db.flush()
# ── Championships ──────────────────────────────────────────────────────
championships_data = [
{
@@ -115,11 +139,12 @@ async def seed():
)
champ = result.scalar_one_or_none()
if champ is None:
champ = Championship(**cd)
champ = Championship(org_id=org.id, **cd)
db.add(champ)
print(f" Created championship: {cd['title']}")
else:
print(f" Championship already exists: {cd['title']}")
champ.org_id = org.id
print(f" Updated championship: {cd['title']}")
await db.commit()
print("\nSeed complete!")