본문 바로가기

Python

[SQLAlchemy] 같은 테이블을 참조하는 방법 (Self-Referential Join)

반응형

같은 테이블 내에서 다른 레코드를 참조해야 하는 경우가 종종 있습니다.

예를 들어, 사용자(user)와 관리자가 모두 동일한 member 테이블에 저장되는 경우, 각 사용자는 관리자 ID를 외래키로 참조하고 있을 수 있습니다.

이러한 구조에서 SQLAlchemy를 통해 자기 참조(joining the same table) 를 수행할 때는 몇 가지 주의할 점이 있습니다.

기본 테이블 구조

예제에서는 관리자가 있는 member 테이블을 사용합니다. 각 member는 선택적으로 admin_id를 가지고 있으며, 이는 같은 테이블 내 다른 member를 참조합니다.

from sqlalchemy import Column, Integer, ForeignKey
from sqlalchemy.orm import relationship, declarative_base

Base = declarative_base()

class Member(Base):
    __tablename__ = 'member'

    id = Column(Integer, primary_key=True)
    admin_id = Column(Integer, ForeignKey('member.id'))

    # 자기 참조 관계
    admin = relationship('Member', remote_side=[id])

여기서 remote_side=[id]를 설정함으로써 SQLAlchemy는 어떤 방향으로 관계를 바라볼지 결정할 수 있습니다.

1. aliased를 사용하는 방법 (relationship 없이도 가능)

SQLAlchemy의 aliased를 사용하면 같은 테이블을 서로 다른 별칭으로 조회할 수 있습니다. 예를 들어, 관리자를 a2, 일반 사용자를 a1이라고 한다면 다음과 같이 사용할 수 있습니다.

from sqlalchemy.orm import aliased

a1 = aliased(Member)
a2 = aliased(Member)

q = (
    session.query(a1, a2)
    .select_from(a1)
    .outerjoin(a2, a2.id == a1.admin_id)
    .filter(a1.admin_id.is_(None))
)

이 방식은 relationship 없이도 가능하며, 복잡한 자기 참조 구조를 단순하게 처리할 수 있다는 장점이 있습니다.

2. relationship을 활용한 ORM 방식 (of_type 사용)

relationship이 선언되어 있는 경우에는 더 간결하게 ORM 스타일로 쿼리를 작성할 수 있습니다. 이때 of_type() 메서드를 사용하여 aliased 객체와의 관계를 명확하게 지정할 수 있습니다.

q = (
    session.query(a1, a2)
    .select_from(a1)
    .outerjoin(a1.admin.of_type(a2))  # a1 → admin (즉 a1.admin_id = a2.id)
    .filter(a1.admin_id.is_(None))
)

이 방법은 SQLAlchemy가 relationship을 이용하여 자동으로 join 조건을 유추하기 때문에 보다 직관적인 코드 작성이 가능합니다.

정리

방식 필요 요소 장점

aliased + 명시적 조건 relationship 없어도 됨 join 조건을 명확히 설정 가능
relationship + of_type() relationship 필요 ORM 스타일로 간결하게 작성 가능

 


 

자기 참조 테이블을 다루는 것은 복잡해 보일 수 있지만, 위와 같이 aliased와 relationship을 적절히 활용하면 깔끔하고 유지보수가 쉬운 코드를 작성할 수 있습니다. SQLAlchemy의 유연함을 활용해 여러분의 모델링을 한층 더 발전시켜 보세요.

반응형