SQLAlchemy에서 backref 및 back_populate 개념
SQLAlchemy는 Python에서 객체 관계 매핑(ORM)을 위한 강력한 프레임워크입니다. ORM은 객체 지향 프로그래밍 방식으로 데이터베이스를 사용할 수 있도록 도와줍니다.
backref
backref
는 관계의 역방향을 정의하는 데 사용됩니다. 예를 들어, Parent
클래스와 Child
클래스 사이에 1:N 관계가 있다고 가정해 보겠습니다. Parent
클래스에서 children
속성을 사용하여 Child
클래스의 인스턴스 목록에 액세스할 수 있습니다.
class Parent(Base):
__tablename__ = "parents"
id = Column(Integer, primary_key=True)
name = Column(String)
children = relationship("Child", backref="parent")
class Child(Base):
__tablename__ = "children"
id = Column(Integer, primary_key=True)
name = Column(String)
parent_id = Column(Integer, ForeignKey("parents.id"))
parent = relationship("Parent")
backref
를 사용하면 Child
클래스에서 parent
속성을 사용하여 Parent
클래스의 인스턴스에 액세스할 수 있습니다.
child = Child.query.get(1)
parent = child.parent
back_populate
back_populate
는 backref
와 유사하지만, 관계의 양쪽 모두에서 속성을 자동으로 생성하는 데 사용됩니다. 위의 예시에서 back_populate
를 사용하면 다음과 같이 코드를 간소화할 수 있습니다.
class Parent(Base):
__tablename__ = "parents"
id = Column(Integer, primary_key=True)
name = Column(String)
children = relationship("Child", back_populate="parent")
class Child(Base):
__tablename__ = "children"
id = Column(Integer, primary_key=True)
name = Column(String)
parent_id = Column(Integer, ForeignKey("parents.id"))
이 코드에서는 Child
클래스에 parent
속성이 자동으로 생성됩니다.
주의 사항
backref
와 back_populate
는 관계를 정의하는 데 유용한 도구이지만, 다음과 같은 몇 가지 주의 사항이 있습니다.
backref
는 관계의 역방향을 정의하는 데만 사용됩니다. 실제 데이터베이스 테이블에는 영향을 미치지 않습니다.back_populate
는 양쪽 모두에서 속성을 자동으로 생성하지만, 외래 키는 여전히Child
클래스에 정의해야 합니다.
예제 코드
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship, sessionmaker
# 데이터베이스 엔진 생성
engine = create_engine("sqlite:///example.db")
# Base 클래스 정의
Base = declarative_base()
# Parent 클래스 정의
class Parent(Base):
__tablename__ = "parents"
id = Column(Integer, primary_key=True)
name = Column(String)
# children 속성 정의: Child 클래스와의 1:N 관계, backref 사용
children = relationship("Child", backref="parent")
# Child 클래스 정의
class Child(Base):
__tablename__ = "children"
id = Column(Integer, primary_key=True)
name = Column(String)
parent_id = Column(Integer, ForeignKey("parents.id"))
# parent 속성 정의: Parent 클래스와의 관계, back_populate 사용
parent = relationship("Parent", back_populate="children")
# 세션 생성
Session = sessionmaker(bind=engine)
session = Session()
# 부모 객체 생성 및 저장
parent = Parent(name="John Doe")
session.add(parent)
# 자식 객체 생성 및 저장, 부모 객체 연결
child1 = Child(name="Jane Doe", parent=parent)
child2 = Child(name="John Doe Jr.", parent=parent)
session.add(child1)
session.add(child2)
# 세션 커밋
session.commit()
# 부모 객체 로드
parent = session.query(Parent).get(1)
# 자식 객체 목록 출력
for child in parent.children:
print(child.name)
# 결과:
# Jane Doe
# John Doe Jr.
이 코드는 다음과 같은 작업을 수행합니다.
sqlite:///example.db
라는 이름의 SQLite 데이터베이스에 연결합니다.Parent
및Child
클래스를 정의합니다.Parent
클래스의children
속성은Child
클래스와의 1:N 관계를 정의하고backref
를 사용하여Child
클래스의parent
속성을 참조합니다.Child
클래스의parent
속성은Parent
클래스와의 관계를 정의하고back_populate
를 사용하여Parent
클래스의children
속성을 참조합니다.Parent
및Child
객체를 생성하고 데이터베이스에 저장합니다.Parent
객체를 로드하고children
속성을 사용하여 자식 객체 목록을 출력합니다.
참고:
- 이 코드는 예시이며 실제 애플리케이션에 맞게 수정해야 할 수 있습니다.
- SQLAlchemy에는 다양한 관계 옵션이 있습니다. 자세한 내용은 SQLAlchemy 문서를 참조하십시오.
SQLAlchemy에서 backref 및 back_populate 대체 방법
명시적 속성 정의
backref
및 back_populate
대신 관계의 역방향을 명시적으로 정의할 수 있습니다. 예를 들어, Parent
클래스와 Child
클래스 사이의 1:N 관계를 다음과 같이 정의할 수 있습니다.
class Parent(Base):
__tablename__ = "parents"
id = Column(Integer, primary_key=True)
name = Column(String)
children = relationship("Child")
class Child(Base):
__tablename__ = "children"
id = Column(Integer, primary_key=True)
name = Column(String)
parent_id = Column(Integer, ForeignKey("parents.id"))
parent = relationship("Parent")
이 코드에서는 Parent
클래스의 children
속성과 Child
클래스의 parent
속성을 명시적으로 정의합니다.
joined_load() 및 subqueryload() 사용
joined_load()
및 subqueryload()
함수를 사용하여 관계의 역방향을 쿼리할 수 있습니다. 예를 들어, Parent
클래스의 모든 자식을 쿼리하려면 다음과 같이 할 수 있습니다.
parents = session.query(Parent).options(joinedload("children")).all()
for parent in parents:
for child in parent.children:
print(child.name)
이 코드는 joined_load()
함수를 사용하여 Parent
객체를 쿼리할 때 children
관계도 함께 로드합니다.
lazy="dynamic" 사용
lazy="dynamic"
옵션을 사용하면 관계의 역방향을 쿼리할 때까지 로드하지 않도록 지연할 수 있습니다. 예를 들어, Parent
클래스의 모든 자식을 쿼리하려면 다음과 같이 할 수 있습니다.
parents = session.query(Parent).options(lazyload("children")).all()
for parent in parents:
children = parent.children
for child in children:
print(child.name)
이 코드는 lazy="dynamic"
옵션을 사용하여 Parent
객체를 쿼리할 때 children
관계는 로드하지 않습니다. children
속성에 처음 접근할 때 실제로 로드됩니다.
hybrid_property() 사용
hybrid_property()
함수를 사용하여 관계의 역방향을 위한 가상 속성을 정의할 수 있습니다. 예를 들어, Parent
클래스의 모든 자식 수를 나타내는 가상 속성을 다음과 같이 정의할 수 있습니다.
from sqlalchemy.orm import hybrid_property
class Parent(Base):
__tablename__ = "parents"
id = Column(Integer, primary_key=True)
name = Column(String)
@hybrid_property
def child_count(self):
return session.query(Child).filter(Child.parent_id==self.id).count()
parent = session.query(Parent).get(1)
print(parent.child_count)
이 코드는 hybrid_property()
함수를 사용하여 child_count
가상 속성을 정의합니다. 이 속성은 Parent
객체의 children
관계에 대한 쿼리를 실행하여 값을 계산합니다.
- 위에 나열된 대체 방법은 각각 장단점이 있습니다.
- 어떤 방법을 사용할지는 특정 상황에 따라 다릅니다.
- SQLAlchemy 문서를 참조하여 다양한 관계 옵션에 대한 자세한 내용을 확인하십시오.
python database sqlalchemy