# Engine
## **create_engine** 함수는 주어진 데이터베이스 URL을 기반으로 **Engine** 객체를 생성합니다.
engine = create_engine(
# Dialect
'postgresql+psycopg2://user:password@localhost/mydatabase',
# Connection Pool
pool_size=10,
max_overflow=20,
pool_timeout=30,
pool_recycle=3600
)
from sqlalchemy import create_engine
from sqlalchemy.orm import Session
engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/")
# create session and add objects
with Session(engine) as session:
session.add(some_object)
session.add(some_other_object)
session.commit()
# OR
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
engine = create_engine("postgresql+psycopg2://scott:tiger@localhost/")
# a sessionmaker(), also in the same scope as the engine
Session = sessionmaker(engine)
# we can now construct a Session() without needing to pass the
# engine each time
with Session() as session:
session.add(some_object)
session.add(some_other_object)
session.commit()
from datetime import datetime
from typing import Optional
from sqlalchemy import ForeignKey
from sqlalchemy import func
from sqlalchemy import Integer
from sqlalchemy import String
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = "user"
id = mapped_column(Integer, primary_key=True)
name: Mapped[str]
fullname: Mapped[Optional[str]]
nickname: Mapped[Optional[str]] = mapped_column(String(64))
create_date: Mapped[datetime] = mapped_column(insert_default=func.now())
addresses: Mapped[List["Address"]] = relationship(back_populates="user")
class Address(Base):
__tablename__ = "address"
id = mapped_column(Integer, primary_key=True)
user_id = mapped_column(ForeignKey("user.id"))
email_address: Mapped[str]
user: Mapped["User"] = relationship(back_populates="addresses")
# 사용하지 않고 매핑하는 경우
user_table = Table(
"user",
Base.metadata,
Column("id", Integer, primary_key=True),
Column("name", String(50)),
Column("fullname", String()),
Column("nickname", String(30)),
)
class SomeClass(Base):
# ...
# will be String() NULL, but type checker will not expect
# the attribute to be None
data: Mapped[str] = mapped_column(nullable=True)
class User(Base):
__tablename__ = "user"
id = mapped_column(Integer, primary_key=True)
name: Mapped[str]
fullname: Mapped[Optional[str]]
nickname: Mapped[Optional[str]] = mapped_column(String(64))
create_date: Mapped[datetime] = mapped_column(insert_default=func.now())
addresses: Mapped[List["Address"]] = relationship(backref="user")
class Address(Base):
__tablename__ = "address"
id = mapped_column(Integer, primary_key=True)
user_id = mapped_column(ForeignKey("user.id"))
email_address: Mapped[str]
# user 를 명시하지 않고 User테이블에서만 정의가 가능한 장점이 있음.
특성 | backref | back_populates |
설정 방식 | 한쪽에서만 정의 | 양쪽에서 명시적으로 정의 |
코드 간결성 | 더 간결함 | 더 명시적 |
자동 생성 | 자동으로 반대편 관계를 생성 | 자동 생성 없음 |
명시적 참조 | 없음 | 관계 설정이 명시적 |
복잡한 관계 처리 | 덜 유연함 | 더 유연하고 복잡한 관계 설정 가능 |
유지보수성 | 작은 프로젝트에 적합 | 큰 프로젝트나 복잡한 관계에 적합 |
#위의 테이블을 봤을때.
with Session() as session:
addr = session.query(Address).first()
addr.user
# 세션이 열려있을때는 접근이 가능하지만 반복된다면
# N+1문제가 발생할 수도 있다.
addr.user # lazy loading 에러 발생
with Session() as session:
addr = session.query(Address).options(joinedload(Address.user)).first()
addr.user
addr.user