다대다 링크로 모델 만들기
다대다 링크로 모델 만들기 (Create Models with a Many-to-Many Link)
이제 이런 링크 테이블을 사용해서 다대다 관계를 지원할 거예요:
클래스 모델을 정의하는 것부터 시작할게요. 링크 테이블 모델도 포함해서요.
출처: 공식문서
링크 테이블 모델
다대다 관계를 지원하고 싶으니, 이제 그 둘을 연결하는 링크 테이블이 필요해요.
다른 SQLModel처럼 그냥 만들면 돼요:
from sqlmodel import Field, Relationship, Session, SQLModel, create_engine
class HeroTeamLink(SQLModel, table=True):
team_id: int | None = Field(default=None, foreign_key="team.id", primary_key=True)
hero_id: int | None = Field(default=None, foreign_key="hero.id", primary_key=True)
이건 다른 것과 똑같은 SQLModel 클래스 모델 테이블이에요.
team_id와 hero_id 두 필드가 있어요.
둘 다 각자 테이블을 가리키는 외래 키예요. 그 모델들은 잠시 후 만들 거지만, 어떻게 동작하는지는 이미 알죠.
그리고 두 필드 모두 기본 키예요. 이건 이전에 써본 적 없던 거예요. 🤓
Team 모델
Team 모델을 볼게요. 이전 버전과 거의 같지만, 약간의 변화가 있어요:
class Team(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str = Field(index=True)
headquarters: str
heroes: list["Hero"] = Relationship(back_populates="teams", link_model=HeroTeamLink)
관계 속성 heroes 는 여전히 히어로의 리스트로, list["Hero"]로 어노테이션되어 있어요. 다시, 코드의 이 시점에는 아직 그 클래스를 선언하지 않았으니 "Hero"를 따옴표로 감싸 써요(하지만 아시다시피 에디터와 SQLModel은 그걸 이해해요).
똑같은 Relationship() 함수를 써요.
back_populates="teams" 를 사용해요. 전에는 team 속성을 참조했지만, 이제 여러 개를 가질 수 있으니 Hero 모델을 만들 때 이름을 teams로 바꿀 거예요.
그리고 다대다 관계를 허용하는 중요한 부분이에요. link_model=HeroTeamLink 를 써요. 그게 전부예요. ✨
Hero 모델
반대쪽을 볼게요. 여기 Hero 모델이 있어요:
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str = Field(index=True)
secret_name: str
age: int | None = Field(default=None, index=True)
teams: list[Team] = Relationship(back_populates="heroes", link_model=HeroTeamLink)
이제 관계가 링크 테이블로 이뤄지니, 이전의 team_id 필드(열)를 제거했어요. 🔥
관계 속성 이름도 이제 여러 팀을 지원하니 team 대신 teams 가 됐어요.
타입도 더는 Team | None이 아니라 팀의 리스트예요. 이제 list[Team] 로 선언하죠.
여기서도 Relationship() 을 써요.
전처럼 back_populates="heroes" 를 여전히 갖고 있어요.
그리고 이제 link_model=HeroTeamLink 가 있어요. ✨
테이블 만들기
이전과 마찬가지로, engine 을 만들고 모든 테이블을 만드는 함수 create_db_and_tables()를 위한 나머지 코드가 있을 거예요:
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
engine = create_engine(sqlite_url, echo=True)
def create_db_and_tables():
SQLModel.metadata.create_all(engine)
그리고 이전 예제들처럼 그 함수를 main() 함수에 추가하고, main 블록에서 그 main() 함수를 호출할게요:
def main():
create_db_and_tables()
create_heroes()
if __name__ == "__main__":
main()
코드 실행하기
커맨드 라인에서 코드를 실행하면 이렇게 출력돼요:
$ uv run python app.py
// 부수적인 코드는 생략했어요 😉
INFO Engine
CREATE TABLE team (
id INTEGER,
name VARCHAR NOT NULL,
headquarters VARCHAR NOT NULL,
PRIMARY KEY (id)
)
INFO Engine [no key 0.00033s] ()
INFO Engine
CREATE TABLE hero (
id INTEGER,
name VARCHAR NOT NULL,
secret_name VARCHAR NOT NULL,
age INTEGER,
PRIMARY KEY (id)
)
INFO Engine [no key 0.00016s] ()
INFO Engine
// 우리의 반짝이는 새 링크 테이블 ✨
CREATE TABLE heroteamlink (
team_id INTEGER,
hero_id INTEGER,
PRIMARY KEY (team_id, hero_id),
FOREIGN KEY(team_id) REFERENCES team (id),
FOREIGN KEY(hero_id) REFERENCES hero (id)
)
INFO Engine [no key 0.00031s] ()
INFO Engine COMMIT
요약
링크 테이블을 선언해서 테이블 사이의 다대다 관계를 지원할 수 있어요.
다른 SQLModel 클래스와 같은 방식으로 만들고, Relationship()의 link_model 매개변수에 쓰면 돼요.
이제 다음 챕터들에서 이 모델들로 데이터를 다뤄 볼게요. 🤓