diff --git a/app/grad_2025/__init__.py b/app/grad_2025/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/app/grad_2025/models.py b/app/grad_2025/models.py new file mode 100644 index 00000000..f5b911e8 --- /dev/null +++ b/app/grad_2025/models.py @@ -0,0 +1,10 @@ +from sqlalchemy.orm import Mapped, mapped_column + +from app.models import Base + + +class Grad2025(Base): + __tablename__ = "grad_2025" + + id: Mapped[int] = mapped_column(primary_key=True, init=False, autoincrement=True) + name: Mapped[str] diff --git a/app/posts/models.py b/app/posts/models.py index d4e0d64c..62f1f45c 100644 --- a/app/posts/models.py +++ b/app/posts/models.py @@ -3,11 +3,13 @@ from sqlalchemy import ( ARRAY, + Column, DateTime, Enum, ForeignKey, Integer, String, + Table, Text, UniqueConstraint, ) @@ -21,6 +23,24 @@ from app.users.models import User +post_grad_2025_table = Table( + "post_grad_2025", + Base.metadata, + Column( + "post_id", + Integer, + ForeignKey("post.id", ondelete="CASCADE"), + primary_key=True, + ), + Column( + "grad_2025_id", + Integer, + ForeignKey("grad_2025.id", ondelete="CASCADE"), + primary_key=True, + ), +) + + class Post(Base): __tablename__ = "post" diff --git a/app/posts/repository.py b/app/posts/repository.py index add9f660..49e1dde6 100644 --- a/app/posts/repository.py +++ b/app/posts/repository.py @@ -1,13 +1,15 @@ from sqlalchemy import String, and_, case, delete, exists, func, or_, select, update +from sqlalchemy.dialects.postgresql import insert from sqlalchemy.orm import joinedload from app.category.enums import Category from app.common.schemas import FeedCursor from app.database.deps import SessionDep +from app.grad_2025.models import Grad2025 from app.users.models import User from app.utils.dependency import dependency -from .models import Post, PostImage +from .models import Post, PostImage, post_grad_2025_table @dependency @@ -124,9 +126,32 @@ async def feed( result = await self.session.scalars(stmt) return list(result.unique().all()) - async def save(self, *, post: Post) -> Post: + async def save(self, *, post: Post, univ_major: str | None = None) -> Post: self.session.add(post) await self.session.flush() + + if univ_major: + grad_2025 = await self.session.scalar( + select(Grad2025).where(Grad2025.name == univ_major) + ) + + if not grad_2025: + grad_2025 = Grad2025(name=univ_major) + self.session.add(grad_2025) + await self.session.flush() + + await self.session.execute( + insert(post_grad_2025_table) + .values(post_id=post.id, grad_2025_id=grad_2025.id) + .on_conflict_do_nothing( + index_elements=[ + post_grad_2025_table.c.post_id, + post_grad_2025_table.c.grad_2025_id, + ] + ) + ) + await self.session.flush() + return post async def update( diff --git a/app/posts/schemas.py b/app/posts/schemas.py index e91d54f0..2e1cc816 100644 --- a/app/posts/schemas.py +++ b/app/posts/schemas.py @@ -113,6 +113,7 @@ def from_post( class PostCreateUpdate(PostBase): images: list[PostImageCreate] = Field(min_length=1) + univ_major: str | None = None class PostImageMeta(APISchema): diff --git a/app/posts/service.py b/app/posts/service.py index 28d5a6f9..4892f9d9 100644 --- a/app/posts/service.py +++ b/app/posts/service.py @@ -161,7 +161,9 @@ async def create_post( ) ) - created_post = await self.post_repository.save(post=created_post) + created_post = await self.post_repository.save( + post=created_post, univ_major=post.univ_major + ) return PostRead.from_post( created_post, diff --git a/migrations/env.py b/migrations/env.py index 296bded9..75037888 100644 --- a/migrations/env.py +++ b/migrations/env.py @@ -8,6 +8,7 @@ import app.auth.models as _ import app.comments.models as _ +import app.grad_2025.models as _ import app.like.tables as _ import app.posts.models as _ import app.tags.models as _ diff --git a/migrations/versions/2025-12-29_add_grad_2025.py b/migrations/versions/2025-12-29_add_grad_2025.py new file mode 100644 index 00000000..03248606 --- /dev/null +++ b/migrations/versions/2025-12-29_add_grad_2025.py @@ -0,0 +1,44 @@ +"""add grad 2025 + +Revision ID: 35d8f758dd7f +Revises: 894cc48da392 +Create Date: 2025-12-29 15:06:53.193867 + +""" + +from typing import Sequence, Union + +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision: str = "35d8f758dd7f" +down_revision: Union[str, None] = "894cc48da392" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table( + "grad_2025", + sa.Column("id", sa.Integer(), autoincrement=True, nullable=False), + sa.Column("name", sa.String(), nullable=False), + sa.PrimaryKeyConstraint("id"), + ) + op.create_table( + "post_grad_2025", + sa.Column("post_id", sa.Integer(), nullable=False), + sa.Column("grad_2025_id", sa.Integer(), nullable=False), + sa.ForeignKeyConstraint(["grad_2025_id"], ["grad_2025.id"], ondelete="CASCADE"), + sa.ForeignKeyConstraint(["post_id"], ["post.id"], ondelete="CASCADE"), + sa.PrimaryKeyConstraint("post_id", "grad_2025_id"), + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table("post_grad_2025") + op.drop_table("grad_2025") + # ### end Alembic commands ### diff --git a/pyproject.toml b/pyproject.toml index 584e2404..7d68452f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,7 +32,7 @@ dev = [ [tool.poe.tasks] setup = "pre-commit install" -dev = "fastapi dev app/main.py" +dev = "fastapi dev app/main.py --host=0.0.0.0" prod = "fastapi run app/main.py --host=0.0.0.0" typecheck = "pyright" lint = "ruff check"