- models: 删除 Profile 模型,字段合并到 User;User 新增云朵/公开/收藏计数器;Cloud 新增 favorite_count - schemas/serializers: 展平 UserOut/AuthOut/MeOut/AdminUserOut,移除嵌套 profile - deps: user.profile.role→user.role,移除 selectinload profile - auth/register: 注册时直接设置 user 字段,不再创建 Profile - clouds: 创建/删除云朵时同步 user.cloud_count 计数器,状态变化时同步 public_cloud_count - favorites: 点赞/取消时同步 cloud.favorite_count 计数器 - admin: 审批/隐藏/批量删时同步 public_cloud_count 计数器 - profiles/stats: 用计数器替代实时 COUNT 查询 - alembic: 新增 migration 合并 profiles 到 users,初始化 counters
96 lines
4.0 KiB
Python
96 lines
4.0 KiB
Python
"""Merge profiles table into users, add counter columns.
|
|
|
|
Revision ID: 20260728_0003
|
|
Revises: 20260726_0002
|
|
Create Date: 2026-07-28
|
|
"""
|
|
|
|
from typing import Sequence
|
|
|
|
from alembic import op
|
|
import sqlalchemy as sa
|
|
|
|
|
|
revision: str = "20260728_0003"
|
|
down_revision: str | None = "20260726_0002"
|
|
branch_labels: str | Sequence[str] | None = None
|
|
depends_on: str | Sequence[str] | None = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
op.add_column("users", sa.Column("username", sa.Text(), nullable=True))
|
|
op.add_column("users", sa.Column("avatar_path", sa.Text(), nullable=True))
|
|
op.add_column("users", sa.Column("role", sa.Text(), server_default="user", nullable=True))
|
|
op.add_column("users", sa.Column("is_disabled", sa.Boolean(), server_default=sa.text("false"), nullable=True))
|
|
op.add_column("users", sa.Column("cloud_count", sa.Integer(), server_default="0", nullable=True))
|
|
op.add_column("users", sa.Column("public_cloud_count", sa.Integer(), server_default="0", nullable=True))
|
|
op.add_column("users", sa.Column("collection_count", sa.Integer(), server_default="0", nullable=True))
|
|
|
|
op.execute("""
|
|
UPDATE users SET
|
|
username = p.username,
|
|
avatar_path = p.avatar_path,
|
|
role = p.role,
|
|
is_disabled = p.is_disabled
|
|
FROM profiles p
|
|
WHERE users.id = p.id
|
|
""")
|
|
|
|
op.alter_column("users", "username", nullable=False)
|
|
op.alter_column("users", "role", nullable=False)
|
|
op.alter_column("users", "is_disabled", nullable=False)
|
|
op.alter_column("users", "cloud_count", nullable=False)
|
|
op.alter_column("users", "public_cloud_count", nullable=False)
|
|
op.alter_column("users", "collection_count", nullable=False)
|
|
|
|
op.create_unique_constraint("uq_users_username", "users", ["username"])
|
|
op.create_check_constraint("ck_users_username_length", "users", "length(trim(username)) >= 2")
|
|
op.create_check_constraint("ck_users_role", "users", "role IN ('user', 'admin')")
|
|
|
|
op.drop_table("profiles")
|
|
|
|
op.add_column("clouds", sa.Column("favorite_count", sa.Integer(), server_default="0", nullable=True))
|
|
op.execute("""
|
|
UPDATE clouds SET favorite_count = (
|
|
SELECT COUNT(*) FROM cloud_favorites WHERE cloud_favorites.cloud_id = clouds.id
|
|
)
|
|
""")
|
|
op.alter_column("clouds", "favorite_count", nullable=False)
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.create_table(
|
|
"profiles",
|
|
sa.Column("id", sa.Uuid(), nullable=False),
|
|
sa.Column("username", sa.Text(), nullable=False),
|
|
sa.Column("avatar_path", sa.Text(), nullable=True),
|
|
sa.Column("role", sa.Text(), server_default="user", nullable=False),
|
|
sa.Column("is_disabled", sa.Boolean(), server_default=sa.text("false"), nullable=False),
|
|
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False),
|
|
sa.Column("updated_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False),
|
|
sa.CheckConstraint("length(trim(username)) >= 2", name="ck_profiles_username_length"),
|
|
sa.CheckConstraint("role IN ('user', 'admin')", name="ck_profiles_role"),
|
|
sa.ForeignKeyConstraint(["id"], ["users.id"], ondelete="CASCADE"),
|
|
sa.PrimaryKeyConstraint("id"),
|
|
sa.UniqueConstraint("username"),
|
|
)
|
|
|
|
op.execute("""
|
|
INSERT INTO profiles (id, username, avatar_path, role, is_disabled, created_at, updated_at)
|
|
SELECT id, username, avatar_path, role, is_disabled, created_at, created_at
|
|
FROM users
|
|
""")
|
|
|
|
op.drop_constraint("ck_users_role", "users", type_="check")
|
|
op.drop_constraint("ck_users_username_length", "users", type_="check")
|
|
op.drop_constraint("uq_users_username", "users", type_="unique")
|
|
op.drop_column("users", "collection_count")
|
|
op.drop_column("users", "public_cloud_count")
|
|
op.drop_column("users", "cloud_count")
|
|
op.drop_column("users", "is_disabled")
|
|
op.drop_column("users", "role")
|
|
op.drop_column("users", "avatar_path")
|
|
op.drop_column("users", "username")
|
|
|
|
op.drop_column("clouds", "favorite_count")
|