유저와 post가 다대다 관계인데, 유저가 먼저 생성되고 post가 추가되는 방식이라고 생각하자.
class User(CreatedMixin, Base):
__tablename__ = "user"
id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), default=uuid.uuid4, primary_key=True
)
username: Mapped[str] = mapped_column(index=True, unique=True)
userpostlinks: Mapped[list["UserPostLink"]] = Relationship( # noqa: F821 # type: ignore
back_populates="user"
)
posts: AssociationProxy[list["Post"]] = association_proxy( # noqa: F821 # type: ignore
"userpostlinks", "post", creator=lambda post_obj: UserPostLink(post=post_obj)
)
class Post(CreatedMixin, Base):
__tablename__ = "post"
id: Mapped[str] = mapped_column(
UUID(as_uuid=True), default=uuid.uuid4, primary_key=True
)
title: Mapped[str]
description: Mapped[str]
status: Mapped[str] = mapped_column(index=True)
userpostlinks: Mapped[list["UserPostLink"]] = Relationship( # noqa: F821 # type: ignore
back_populates="post"
)
users: AssociationProxy[list["User"]] = association_proxy( # noqa: F821 # type: ignore
"userpostlinks", "user"
)
class UserPostLink(Base, CreatedMixin):
__tablename__ = "userpostlink"
user_id: Mapped[uuid.UUID] = mapped_column(ForeignKey("user.id"), primary_key=True)
post_id: Mapped[str] = mapped_column(ForeignKey("post.id"), primary_key=True)
user: Mapped["User"] = Relationship(back_populates="userpostlinks") # type: ignore # noqa: F821
post: Mapped["Post"] = Relationship(back_populates="userpostlinks") # type: ignore # noqa: F821
이런식으로 해두면 user에서 userpostlink를 거치지 않고 바로 post로 갈 수 있음. 또한 orm 방식을 사용해서 user에다 post를 추가할때, creator를 설정해두었기 때문에 userpostlink를 통해 생성됨. User의 posts를 잘 보면 userpostlink의 post를 향해 프록시를 설정한다는 것.
나중에 ide(mypy)의 도움을 받기 위해서는 타입을 명확히 해줘야하는데, 무턱대고 import를 해버리면 circular import때문에 문제가된다. 먼저 큰 따옴표 안에 가둬두고 아래와 같은 코드를 추가해주자
if TYPE_CHECKING:
from app.model.relationships import UserPostLink
from app.model.post import Post
from app.model.relationships import UserPostLinkUserPostLink는 creator에 사용되기 때문에 직접 import 했음. type_checking은 typing 모듈에서 지원하는데, 이친구는 runtime에는 false임. mypy를 돌릴때만 true가 되기때문에 ide의 도움을 받을 수 있다.
