5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

SQLAlchemy1.3から1.4に移行

Last updated at Posted at 2021-03-18

SQLAlchemy1.4が2021/3/17にリリースされた。
1.3.19 -> 1.4.1 に変更する際に調査したことをメモする。

Sessionがコンテキストマネージャに

1.3では

Sessionを使用する際に、close()することを忘れてはいけない。
そのため、try~finallyや以下のようなコンテキストマネージャを作成して、with構文で使用するようにしていた。

from contextlib import contextmanager

@contextmanager
def session_scope():
    """Provide a transactional scope around a series of operations."""
    session = Session()
    try:
        yield session
        session.commit()
    except:
        session.rollback()
        raise
    finally:
        session.close()

1.4では

Sessionにコンテキストマネージャが実装されているのでそのままwith構文で使用でき、closeが行われる。

with Session(engine) as session:
    try:
        session.add(some_object)
        session.add(some_other_object)
    except:
        session.rollback()
        raise
    else:
        session.commit()

ちなみにsession.begin()はcommitやrollbackを実装しているので以下のようにするとさらに便利。

# create session and add objects
with Session(engine) as session:
    with session.begin():
      session.add(some_object)
      session.add(some_other_object)
    # inner context calls session.commit(), if there were no exceptions
# outer context calls session.close()
# create session and add objects
with Session(engine) as session, session.begin():
    session.add(some_object)
    session.add(some_other_object)
# inner context calls session.commit(), if there were no exceptions
# outer context calls session.close()

##アノテーションでのマッピングが可能に

1.3では

以下2つの方法でマッピングが行えた。

Declarative Mapping


Base = declarative_base()

class User(Base):
    __tablename__ = 'user'

    id = Column(Integer, primary_key=True)
    name = Column(String)
    fullname = Column(String)
    nickname = Column(String)

Classical Mapping


metadata = MetaData()

user = Table('user', metadata,
            Column('id', Integer, primary_key=True),
            Column('name', String(50)),
            Column('fullname', String(50)),
            Column('nickname', String(12))
        )

class User(object):
    def __init__(self, name, fullname, nickname):
        self.name = name
        self.fullname = fullname
        self.nickname = nickname

mapper(User, user)

私のプロジェクトでは以下のように実装していたが、mypyエラーに引っ掛かっていた。
UserとUserModelの属性の型が異なるというエラーである。

@dataclass
class User:
    id: int = field(init=False)
    name: Optional[str] = None
    fullname: Optional[str] = None
    nickname: Optional[str] = None


Base = declarative_base()

class UserModel(Base, User):
    __tablename__ = 'user'

    id = Column(Integer, primary_key=True)
    name = Column(String)
    fullname = Column(String)
    nickname = Column(String)

1.4では

Declarative Mapping
@mapper_registry.mappedというアノテーションを利用した方法が追加された。

@mapper_registry.mapped
class UserModel(User):
    __table__ = Table(
        "user",
        mapper_registry.metadata,
        Column("id", Integer, primary_key=True),
        Column("name", String(50)),
        Column("fullname", String(50)),
        Column("nickname", String(12)),
    )
    id: int = field(init=False)
    name: Optional[str] = None
    fullname: Optional[str] = None
    nickname: Optional[str] = None

このアノテーションを使用することで1.3の時に起きていたmypyエラーがなくなった。理由としては

  • @dataclassがマッピングを実行する前に処理される。
  • 内部で実装されているmapperも@dataclassの固有の属性を検出し、マッピング時にそれらを置き換えるようになった。

Classical Mapping

mapperを使用することが非推奨となった。代わりにmap_imperativelyを使用する。

mapper_registry = registry()

user_table = Table(
    'user',
    mapper_registry.metadata,
    Column('id', Integer, primary_key=True),
    Column('name', String(50)),
    Column('fullname', String(50)),
    Column('nickname', String(12))
)

class User:
    pass

mapper_registry.map_imperatively(User, user_table)

##おまけ

sqlalchemy2-stubsがデフォルトで内部に組み込まれた。
そのため1.3の時のようにerror: Invalid base class "Base"というmypyエラーを回避するために
sqlalchemy-stubsなどのスタブをわざわざインストールする必要もなくなった。

5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?