본문 바로가기
Programming/python

[Python] SqlAlchemy relation 설정하기

by 유주원 2017. 9. 1.

실제 DB에 설정되어 있는 관계들을 SqlAlchemy의 ORM에서 명시적으로 선언해 줌으로써 편하게 object 형태로 사용할 수가 있다.

혹시 sqlAlchemy에 대해 잘 모르겠다.. 하시는 분들은 아래의 링크를 먼저 보고 오면 도움이 될 수 있을 것 같다.

http://yujuwon.tistory.com/entry/SQLAlchemy-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0

RDB에 USER_TB, ADDRESS_TB란 이름의 테이블이 생성되어 있다고 가정하면 아래와 같이 실제 DB 테이블에 매핑할 수 있는 class들을 선언할 수 있다.

from sqlalchemy import Column, Integer, String, DateTime, ForeignKey

from database import Base


class TbUser(Base):

    __tablename__ = 'USER_TB'

    id = Column(Integer, primary_key=True)

    name = Column(String(64))


class TbAddress(Base):

    __tablename__ = 'ADDRESS_TB'

    id = Column(Integer, primary_key=True)

    user_id = Column(Integer, ForeignKey('USER_TB.id'))

    user = relationship("TbUser", backref=backref("addresses", order_by=id))

실제 DB 상에서도 하나의  User가 여러 개의 address를 가질 수 있도록 1:N의 구조로 테이블이 생성되어져 있고, ORM에서도 이를 맞춰주기 위해 userid를 기준으로 ForeignKey를 생성하였다. 

주의할 사항이 하나 있는데 sqlalchemy에서 ForeignKey를 맞춰주기 위해 기준이 되는 키를 선택할 때 (여기서는 USER_TB.id) 반드시 실제 DB 테이블 명을 넣어주어야 한다. 나같은 경우에는 해당 클래스 명을 넣어줬더니 계속 binding 에러가 나서 고생한 적이 있다.

또한 TbAddress 클래스를 보면 user란 변수가 있고, relationship 객체로 생성되어져 있는 것을 볼 수가 있다. 이는 실제 DB 컬럼으로 존재하는 변수는 아니지만 코드 상에서 쉽게 접근할 수 있도록 하기 위해 설정이 되었다. 즉 TbAddress 객체에서 해당 부모 객체인 TbUser를 쉽게 찾아갈 수가 있다. relationship 선언 시 파라미터를 보면 backref란 항목이 있는 것을 볼 수가 있다. 이는 그 반대의 상황 즉 TbUser 객체에서 자식 객체인 TbAddress들을 쉽게 찾을 수 있도록 설정을 구성한 것이라 생각하면 된다. 당연히 TbAddress에 이미 등록된 column이름으로 사용하면 에러가 날 것이다. (만약 TbAddress에 addresses라는 String column이 존재하고 있고 backref 이름도 addresses라고 이름 붙였다면 TbUser 클래스 입장에서는 혼란이 발생할 것이다.)

아래의 예를 살펴보면 이해가 더욱 쉬울 것 같다.

queries = db_session.query(TbUser)

datas = [dict(id=q.id, addresses = q.addresses) for q in queries[0:10]]

print(datas)

TbUser 클래스에 addresses란 변수가 없음에도 불구하고, 우리는 위와 같은 query를 통해 해당 user가 가지고 있는 address 리스트 들을 확인해 볼 수가 있다. 해당 기능은 relationship의 backref를 통해 지원이 가능하다.

또한 우리는 아래처럼 address class에서 해당 부모 user를 확인할 수도 있다.

queries = db_session.query(TbAddress)

datas = [dict(id=q.id, user = q.user) for q in queries[0:10]]

print(datas)

DB에 데이터 삽입시에도 User item 따로 address item 따로 입력할 필요 없이 아래와 같이 사용할 수도 있다.

User = TbUser(userInfo)

User.addresses = [TbAddress(addressInfo1), TbAddress(addressInfo2)]

db_session.add(User)

db_session.commit()