From 63e87a3ed84ee9e9e4a4ff329a40d2b7ce5e5d0d Mon Sep 17 00:00:00 2001 From: l3wdfut4pwr Date: Sat, 4 Apr 2026 00:03:04 +0300 Subject: add profile --- app/models/__init__.py | 5 +++++ app/models/collections.py | 40 ---------------------------------------- app/models/image.py | 9 ++++++--- app/models/integrations.py | 13 +++++++++++-- app/models/profile.py | 38 +++++++++++++++++++++++++++++++++++--- app/models/user.py | 33 +++++++++++++++++++++++++-------- 6 files changed, 82 insertions(+), 56 deletions(-) (limited to 'app/models') diff --git a/app/models/__init__.py b/app/models/__init__.py index e69de29..603a57f 100644 --- a/app/models/__init__.py +++ b/app/models/__init__.py @@ -0,0 +1,5 @@ +# from .collections import Collection +from .image import Image +from .integrations import UserIntegration +from .profile import Profile +from .user import User diff --git a/app/models/collections.py b/app/models/collections.py index 7dc5bb7..e69de29 100644 --- a/app/models/collections.py +++ b/app/models/collections.py @@ -1,40 +0,0 @@ -from __future__ import annotations - -from datetime import datetime -from typing import TYPE_CHECKING - -from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String, Table -from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship - -Base = DeclarativeBase() - -if TYPE_CHECKING: - from .image import Image - -collection_images = Table( - "collection_images", - Base.metadata, - mapped_column( - "collection_id", Integer, ForeignKey("collections.id"), primary_key=True - ), - mapped_column("image_id", Integer, ForeignKey("images.id"), primary_key=True), -) - - -class Collection(Base): - __tablename__ = "collections" - - id: Mapped[int] = mapped_column(Integer, primary_key=True) - user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), nullable=False) - name: Mapped[str] = mapped_column(String(100), nullable=False) - description: Mapped[str | None] = mapped_column(String(500), nullable=True) - is_private: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) - created_at: Mapped[datetime] = mapped_column( - DateTime(timezone=True), default=datetime.utcnow - ) - - images: Mapped[list["Image"]] = relationship( - "Image", - secondary=collection_images, - back_populates="collections", - ) diff --git a/app/models/image.py b/app/models/image.py index e139123..b75b197 100644 --- a/app/models/image.py +++ b/app/models/image.py @@ -12,11 +12,12 @@ from sqlalchemy import ( Table, func, ) -from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship +from sqlalchemy.orm import Mapped, mapped_column, relationship -from .collections import Collection +from app.utils.db import Base + +# from .collections import Collection -Base = DeclarativeBase() image_tags = Table( "image_tags", @@ -63,8 +64,10 @@ class Image(Base): ) +""" collections: Mapped[list["Collection"]] = relationship( "Collection", secondary="collection_images", back_populates="images", ) +""" diff --git a/app/models/integrations.py b/app/models/integrations.py index 4a1ddb9..f3eaca4 100644 --- a/app/models/integrations.py +++ b/app/models/integrations.py @@ -1,7 +1,12 @@ +from typing import TYPE_CHECKING + from sqlalchemy import ForeignKey, Integer, String -from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column +from sqlalchemy.orm import Mapped, mapped_column, relationship + +from app.utils.db import Base -Base = DeclarativeBase() +if TYPE_CHECKING: + from .user import User class UserIntegration(Base): @@ -17,3 +22,7 @@ class UserIntegration(Base): x: Mapped[str | None] = mapped_column(String(255), nullable=True) # Twitter/X behance: Mapped[str | None] = mapped_column(String(255), nullable=True) instagram: Mapped[str | None] = mapped_column(String(255), nullable=True) + + user: Mapped["User"] = relationship( + "User", back_populates="integrations", uselist=False + ) diff --git a/app/models/profile.py b/app/models/profile.py index b19d796..93ce6fd 100644 --- a/app/models/profile.py +++ b/app/models/profile.py @@ -1,14 +1,28 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from fastapi import Depends from sqlalchemy import ForeignKey, Integer, String -from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column +from sqlalchemy.ext.asyncio import AsyncSession +from sqlalchemy.future import select +from sqlalchemy.orm import Mapped, mapped_column, relationship + +from app.utils.db import Base, get_async_session -Base = DeclarativeBase() +if TYPE_CHECKING: + from .user import ( + User, + ) class Profile(Base): __tablename__ = "profiles" id: Mapped[int] = mapped_column(Integer, primary_key=True) - user_id: Mapped[int] = mapped_column(ForeignKey("users.id"), unique=True) + user_id: Mapped[int] = mapped_column( + ForeignKey("users.id"), unique=True, nullable=False + ) avatar_file: Mapped[str | None] = mapped_column(String(255), nullable=True) banner_file: Mapped[str | None] = mapped_column(String(255), nullable=True) @@ -19,3 +33,21 @@ class Profile(Base): subscriptions_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False) followers_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False) following_count: Mapped[int] = mapped_column(Integer, default=0, nullable=False) + + user: Mapped["User"] = relationship( + "User", back_populates="profile", uselist=False, lazy="selectin" + ) + + @classmethod + async def get_by_user_id( + cls, user_id: int, session: AsyncSession = Depends(get_async_session) + ): + result = await session.execute(select(cls).where(cls.user_id == user_id)) + return result.scalars().first() + + @classmethod + async def get_by_id( + cls, profile_id: int, session: AsyncSession = Depends(get_async_session) + ): + result = await session.execute(select(cls).where(cls.id == profile_id)) + return result.scalars().first() diff --git a/app/models/user.py b/app/models/user.py index 8dbe419..442173c 100644 --- a/app/models/user.py +++ b/app/models/user.py @@ -1,14 +1,18 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + from fastapi import Depends from sqlalchemy import Boolean, Integer, String from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.future import select -from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column - -from app.utils.db import get_async_session +from sqlalchemy.orm import Mapped, mapped_column, relationship, selectinload +from app.utils.db import Base, get_async_session -class Base(DeclarativeBase): - pass +if TYPE_CHECKING: + from .integrations import UserIntegration + from .profile import Profile class User(Base): @@ -21,13 +25,19 @@ class User(Base): google_id: Mapped[str | None] = mapped_column( String(255), unique=True, nullable=True ) - avatar_file: Mapped[str | None] = mapped_column(String(255), nullable=True) - banner_file: Mapped[str | None] = mapped_column(String(255), nullable=True) + description: Mapped[str | None] = mapped_column(String(250), nullable=True) premium: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) is_banned: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) is_moderator: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) token_version: Mapped[int] = mapped_column(Integer, default=0, nullable=False) + profile: Mapped["Profile"] = relationship( + "Profile", back_populates="user", uselist=False, lazy="selectin" + ) + integrations: Mapped["UserIntegration"] = relationship( + "UserIntegration", back_populates="user", uselist=False, lazy="selectin" + ) + @classmethod async def get_user_by_email( cls, email: str, session: AsyncSession = Depends(get_async_session) @@ -39,7 +49,14 @@ class User(Base): async def get_user_by_username( cls, username: str, session: AsyncSession = Depends(get_async_session) ): - result = await session.execute(select(cls).where(cls.username == username)) + result = await session.execute( + select(cls) + .options( + selectinload(cls.profile), + selectinload(cls.integrations), + ) + .where(cls.username == username) + ) return result.scalars().first() @classmethod -- cgit v1.3-3-g829e