타입 어노테이션 문자열
타입 어노테이션 문자열 (Type annotation strings)
처음 관계 속성에서 heroes를 list["Hero"]로 선언하면서 Hero를 그냥 평범하게 쓰지 않고 따옴표로 감쌌던 걸 기억하시나요? 이 장에서는 그것이 왜 그런지, 그리고 어떤 의미가 있는지 자세히 살펴볼게요.
출처: 공식문서
list["Hero"]의 문자열에 대하여
첫 번째 관계(Relationship) 속성에서 list["Hero"]로 선언했어요. Hero를 그냥 평범하게 두지 않고 따옴표로 감쌌죠:
from sqlmodel import Field, Relationship, Session, SQLModel, create_engine
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="team")
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)
team_id: int | None = Field(default=None, foreign_key="team.id")
team: Team | None = Relationship(back_populates="heroes")
# Code below omitted 👇
이건 대체 왜 그런 걸까요? 그냥 평범하게 list[Hero]라고 쓰면 안 될까요?
바로 그 코드 줄에서, Python 인터프리터는 아직 Hero라는 클래스가 뭔지 모르기 때문이에요. 그걸 그냥 그대로 두면 인터프리터가 Hero를 찾으려 시도하다가 실패하고, 프로그램이 멈춰요. 😭
하지만 따옴표 안에 문자열로 넣으면, 인터프리터는 그걸 그냥 안에 "Hero"라는 텍스트가 든 문자열로 보기만 해요.
하지만 에디터나 다른 도구들은 그 문자열이 실제로 안에 타입 어노테이션을 담고 있다는 걸 알아보고, 자동 완성·타입 검사 등을 제공해 줘요. 🎉
그리고 당연히 SQLModel도 그 문자열을 올바르게 이해해요. ✨
이것은 사실 Python 자체의 기능이에요. 이런 상황을 처리하기 위한 현재의 공식적인 해결책이죠.
📌 참고
Python 안에서도 이것을 더 단순하고 직관적으로 만들고, 클래스를 문자열로 감싸지 않아도 되는 방법을 찾는 작업이 많이 진행 중이에요.
👀 전체 파일 미리보기 (Full file preview)
from sqlmodel import Field, Relationship, Session, SQLModel, create_engine
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="team")
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)
team_id: int | None = Field(default=None, foreign_key="team.id")
team: Team | None = Relationship(back_populates="heroes")
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)
def create_heroes():
with Session(engine) as session:
team_preventers = Team(name="Preventers", headquarters="Sharp Tower")
team_z_force = Team(name="Z-Force", headquarters="Sister Margaret's Bar")
hero_deadpond = Hero(
name="Deadpond", secret_name="Dive Wilson", team=team_z_force
)
hero_rusty_man = Hero(
name="Rusty-Man", secret_name="Tommy Sharp", age=48, team=team_preventers
)
hero_spider_boy = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
session.add(hero_deadpond)
session.add(hero_rusty_man)
session.add(hero_spider_boy)
session.commit()
session.refresh(hero_deadpond)
session.refresh(hero_rusty_man)
session.refresh(hero_spider_boy)
print("Created hero:", hero_deadpond)
print("Created hero:", hero_rusty_man)
print("Created hero:", hero_spider_boy)
hero_spider_boy.team = team_preventers
session.add(hero_spider_boy)
session.commit()
def main():
create_db_and_tables()
create_heroes()
if __name__ == "__main__":
main()