コンテンツにスキップ

データの読み取り - SELECT

既にデータベースとテーブルがあり、以下のようなデータが含まれています。

id名前秘密の名前年齢
1デッドポンドダイブ・ウィルソンnull
2スパイダーボーイペドロ・パルケアドールnull
3ラスティマントミー・シャープ48

どんどん面白くなってきました!データベースからデータを読み取る方法を見てみましょう!🤩

前のコードから続行する

データを生成するために使用した最後のコードから続行しましょう。

👀 ファイル全体のプレビュー
from sqlmodel import Field, Session, SQLModel, create_engine


class Hero(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: int | None = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        session.commit()


def main():
    create_db_and_tables()
    create_heroes()


if __name__ == "__main__":
    main()
from typing import Optional

from sqlmodel import Field, Session, SQLModel, create_engine


class Hero(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: Optional[int] = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        session.commit()


def main():
    create_db_and_tables()
    create_heroes()


if __name__ == "__main__":
    main()

**SQLModel** Heroクラスモデルを作成し、いくつかのレコードを作成しています。

Heroモデルと**engine**が必要になりますが、新しい関数でデータをクエリするために新しいセッションを作成します。

SQLでデータを読み取る

Pythonコードを書く前に、SQLでデータをクエリする方法を簡単に復習しましょう。

SELECT id, name, secret_name, age
FROM hero

大体以下のようになります。

SQLデータベースさん👋、データをSELECTしてください。

最初に必要な列を指定します。

  • id
  • 名前
  • 秘密の名前
  • 年齢

そして、"hero"というテーブルから取得してほしいです。

すると、データベースはデータを取得し、次のようなテーブル形式で返します。

id名前秘密の名前年齢
1デッドポンドダイブ・ウィルソンnull
2スパイダーボーイペドロ・パルケアドールnull
3ラスティマントミー・シャープ48

**DB Browser for SQLite**で試すことができます。

警告

ここではすべての行を取得しています。

数千行ある場合、データベースの計算コストが高くなる可能性があります。

通常は、必要な行のみを受信するように行をフィルタリングします。しかし、それについては次の章で学びます。

SQLのショートカット

上記の例のようにすべての列を取得する場合、SQLにはショートカットがあり、各列名を指定する代わりに*を書くことができます。

SELECT *
FROM hero

結果は同じになります.ただし、**SQLModel**では使用しません.

より少ない列をSELECTする

例えば、以下のように少ない列をSELECTすることもできます。

SELECT id, name
FROM hero

ここでは、idname列のみを選択しています。

そして、次のようなテーブルになります。

id名前
1デッドポンド
2スパイダーボーイ
3ラスティマン

ここで興味深いことに、SQLデータベースはデータをテーブルに格納します。そして、常に結果を**テーブル**で伝えます。

SELECTのバリエーション

SQL言語では、いくつかの場所で**バリエーション**が許容されます。

そのバリエーションの1つは、SELECTステートメントで列名を直接使用するか、テーブル名とドットを前に付けて使用できることです。

例えば、上記のSQLコードは次のように書くこともできます。

SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero

これは、後で同じ名前の列を持つ複数のテーブルを同時に操作する場合に特に重要になります。

例えば、hero.idteam.id、またはhero.nameteam.nameです。

もう1つのバリエーションは、SELECTのようなほとんどのSQLキーワードは、selectのように小文字で書くこともできることです。

結果テーブルは存在する必要がない

これが興味深い部分です。SQLデータベースによって返されるテーブルは、データベースに独立したテーブルとして**存在する必要はありません**。 🧙

例えば、私たちのデータベースには、idnamesecret_nameageのすべての列を持つテーブルが1つだけあります。そしてここでは、より少ない列を持つ結果テーブルを取得しています.

SQLの主要なポイントの1つは、データを異なるテーブルに構造化し、データを繰り返すことなく、さまざまな方法でデータベースをクエリし、結果としてさまざまなテーブルを取得できることです。

**SQLModel**でデータを読み取る

それでは、すべてのヒーローを読み取るための同じクエリを**SQLModel**で行ってみましょう。

**Session**を作成する

最初のステップは、行を作成したときと同じように**Session**を作成することです。

新しい関数select_heroes()でそれを始めます。

# Code above omitted 👆

def select_heroes():
    with Session(engine) as session:

# More code here later 👇
# Code above omitted 👆

def select_heroes():
    with Session(engine) as session:

# More code here later 👇
👀 ファイル全体のプレビュー
from sqlmodel import Field, Session, SQLModel, create_engine, select


class Hero(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: int | None = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        session.commit()


def select_heroes():
    with Session(engine) as session:
        statement = select(Hero)
        results = session.exec(statement)
        for hero in results:
            print(hero)


def main():
    create_db_and_tables()
    create_heroes()
    select_heroes()


if __name__ == "__main__":
    main()
from typing import Optional

from sqlmodel import Field, Session, SQLModel, create_engine, select


class Hero(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: Optional[int] = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        session.commit()


def select_heroes():
    with Session(engine) as session:
        statement = select(Hero)
        results = session.exec(statement)
        for hero in results:
            print(hero)


def main():
    create_db_and_tables()
    create_heroes()
    select_heroes()


if __name__ == "__main__":
    main()

selectステートメントを作成する

次に、上記のSQL SELECTステートメントを書いたのとほぼ同じ方法で、**SQLModel** selectステートメントを作成します。

最初に、ファイルの先頭でsqlmodelからselectをインポートする必要があります。

from sqlmodel import Field, Session, SQLModel, create_engine, select

# More code below omitted 👇
from typing import Optional

from sqlmodel import Field, Session, SQLModel, create_engine, select

# More code below omitted 👇
👀 ファイル全体のプレビュー
from sqlmodel import Field, Session, SQLModel, create_engine, select


class Hero(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: int | None = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        session.commit()


def select_heroes():
    with Session(engine) as session:
        statement = select(Hero)
        results = session.exec(statement)
        for hero in results:
            print(hero)


def main():
    create_db_and_tables()
    create_heroes()
    select_heroes()


if __name__ == "__main__":
    main()
from typing import Optional

from sqlmodel import Field, Session, SQLModel, create_engine, select


class Hero(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: Optional[int] = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        session.commit()


def select_heroes():
    with Session(engine) as session:
        statement = select(Hero)
        results = session.exec(statement)
        for hero in results:
            print(hero)


def main():
    create_db_and_tables()
    create_heroes()
    select_heroes()


if __name__ == "__main__":
    main()

そして、それを使用してPythonコードでSELECTステートメントを作成します。

from sqlmodel import Field, Session, SQLModel, create_engine, select

# More code here omitted 👈

def select_heroes():
    with Session(engine) as session:
        statement = select(Hero)

# More code here later 👇
from typing import Optional

from sqlmodel import Field, Session, SQLModel, create_engine, select

# More code here omitted 👈

def select_heroes():
    with Session(engine) as session:
        statement = select(Hero)

# More code here later 👇
👀 ファイル全体のプレビュー
from sqlmodel import Field, Session, SQLModel, create_engine, select


class Hero(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: int | None = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        session.commit()


def select_heroes():
    with Session(engine) as session:
        statement = select(Hero)
        results = session.exec(statement)
        for hero in results:
            print(hero)


def main():
    create_db_and_tables()
    create_heroes()
    select_heroes()


if __name__ == "__main__":
    main()
from typing import Optional

from sqlmodel import Field, Session, SQLModel, create_engine, select


class Hero(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: Optional[int] = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        session.commit()


def select_heroes():
    with Session(engine) as session:
        statement = select(Hero)
        results = session.exec(statement)
        for hero in results:
            print(hero)


def main():
    create_db_and_tables()
    create_heroes()
    select_heroes()


if __name__ == "__main__":
    main()

これは、多くの情報を伝える非常に簡単なコード行です。

statement = select(Hero)

これは、上記の最初のSQL SELECTステートメントと同等です。

SELECT id, name, secret_name, age
FROM hero

クラスモデルHeroselect()関数に渡します。これは、Heroクラスに必要なすべての列を選択することを指示します。

また、select()関数では、FROM部分を明示的に指定していないことに注意してください。**SQLModel**(実際にはSQLAlchemy)にとって、heroテーブルから選択したいことは既に明らかです。なぜなら、それがHeroクラスモデルに関連付けられているテーブルだからです。

ヒント

select()によって返されるstatementの値は、他のことを行うことを可能にする特別なオブジェクトです。

それについては、次の章で説明します。

ステートメントを実行する

これでselectステートメントができたので、**session**で実行できます。

# Code above omitted 👆

def select_heroes():
    with Session(engine) as session:
        statement = select(Hero)
        results = session.exec(statement)

# More code here later 👇
# Code above omitted 👆

def select_heroes():
    with Session(engine) as session:
        statement = select(Hero)
        results = session.exec(statement)

# More code here later 👇
👀 ファイル全体のプレビュー
from sqlmodel import Field, Session, SQLModel, create_engine, select


class Hero(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: int | None = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        session.commit()


def select_heroes():
    with Session(engine) as session:
        statement = select(Hero)
        results = session.exec(statement)
        for hero in results:
            print(hero)


def main():
    create_db_and_tables()
    create_heroes()
    select_heroes()


if __name__ == "__main__":
    main()
from typing import Optional

from sqlmodel import Field, Session, SQLModel, create_engine, select


class Hero(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: Optional[int] = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        session.commit()


def select_heroes():
    with Session(engine) as session:
        statement = select(Hero)
        results = session.exec(statement)
        for hero in results:
            print(hero)


def main():
    create_db_and_tables()
    create_heroes()
    select_heroes()


if __name__ == "__main__":
    main()

これは、**session**に、**engine**を使用してデータベースでそのSELECTステートメントを実行し、結果を返すように指示します。

echo=Trueで**engine**を作成したため、実行するSQLが出力に表示されます。

このsession.exec(statement)は、次の出力を生成します。

INFO Engine BEGIN (implicit)
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
INFO Engine [no key 0.00032s] ()

データベースは、SQLを直接書いたときと同様に、すべてのデータを含むテーブルを返します。

id名前秘密の名前年齢
1デッドポンドダイブ・ウィルソンnull
2スパイダーボーイペドロ・パルケアドールnull
3ラスティマントミー・シャープ48

結果を反復処理する

resultsオブジェクトは、各行を処理するために使用できるイテラブルです。

これで、forループに入れて、各ヒーローを出力できます。

# Code above omitted 👆

def select_heroes():
    with Session(engine) as session:
        statement = select(Hero)
        results = session.exec(statement)
        for hero in results:
            print(hero)

# Code below omitted 👇
# Code above omitted 👆

def select_heroes():
    with Session(engine) as session:
        statement = select(Hero)
        results = session.exec(statement)
        for hero in results:
            print(hero)

# Code below omitted 👇
👀 ファイル全体のプレビュー
from sqlmodel import Field, Session, SQLModel, create_engine, select


class Hero(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: int | None = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        session.commit()


def select_heroes():
    with Session(engine) as session:
        statement = select(Hero)
        results = session.exec(statement)
        for hero in results:
            print(hero)


def main():
    create_db_and_tables()
    create_heroes()
    select_heroes()


if __name__ == "__main__":
    main()
from typing import Optional

from sqlmodel import Field, Session, SQLModel, create_engine, select


class Hero(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: Optional[int] = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        session.commit()


def select_heroes():
    with Session(engine) as session:
        statement = select(Hero)
        results = session.exec(statement)
        for hero in results:
            print(hero)


def main():
    create_db_and_tables()
    create_heroes()
    select_heroes()


if __name__ == "__main__":
    main()

これは、次の出力を出力します。

id=1 name='Deadpond' age=None secret_name='Dive Wilson'
id=2 name='Spider-Boy' age=None secret_name='Pedro Parqueador'
id=3 name='Rusty-Man' age=48 secret_name='Tommy Sharp'

select_heroes()main()に追加する

コマンドラインからプログラムを実行したときに実行されるように、main()関数にselect_heroes()の呼び出しを含めます。

# Code above omitted 👆

def select_heroes():
    with Session(engine) as session:
        statement = select(Hero)
        results = session.exec(statement)
        for hero in results:
            print(hero)


def main():
    create_db_and_tables()
    create_heroes()
    select_heroes()

# More code here later 👇
# Code above omitted 👆

def select_heroes():
    with Session(engine) as session:
        statement = select(Hero)
        results = session.exec(statement)
        for hero in results:
            print(hero)


def main():
    create_db_and_tables()
    create_heroes()
    select_heroes()

# More code here later 👇
👀 ファイル全体のプレビュー
from sqlmodel import Field, Session, SQLModel, create_engine, select


class Hero(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: int | None = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        session.commit()


def select_heroes():
    with Session(engine) as session:
        statement = select(Hero)
        results = session.exec(statement)
        for hero in results:
            print(hero)


def main():
    create_db_and_tables()
    create_heroes()
    select_heroes()


if __name__ == "__main__":
    main()
from typing import Optional

from sqlmodel import Field, Session, SQLModel, create_engine, select


class Hero(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: Optional[int] = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        session.commit()


def select_heroes():
    with Session(engine) as session:
        statement = select(Hero)
        results = session.exec(statement)
        for hero in results:
            print(hero)


def main():
    create_db_and_tables()
    create_heroes()
    select_heroes()


if __name__ == "__main__":
    main()

コードを確認する

素晴らしい、これでデータベースからデータを読み取ることができました!🎉

ここまでのコードを確認しましょう。

from sqlmodel import Field, Session, SQLModel, create_engine, select  # (1)!


class Hero(SQLModel, table=True):  # (2)!
    id: int | None = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: int | None = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)  # (3)!


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)  # (4)!


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")  # (5)!
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    with Session(engine) as session:  # (6)!
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        session.commit()


def select_heroes():
    with Session(engine) as session:  # (7)!
        statement = select(Hero)  # (8)!
        results = session.exec(statement)  # (9)!
        for hero in results:  # (10)!
            print(hero)  # (11)!
    # (12)!


def main():
    create_db_and_tables()
    create_heroes()
    select_heroes()  # (13)!


if __name__ == "__main__":
    main()
  1. 新しいselect()関数を含む、使用するすべてのものをsqlmodelからインポートします。

  2. heroテーブルを表すHeroクラスモデルを作成します。

  3. **engine**を作成します。アプリケーションコード全体で共有される1つのエンジンを使用する必要があり、ここでそれを行っています。

  4. SQLModel.metadataに登録されているモデルのすべてのテーブルを作成します。

    これは、データベースがまだ存在しない場合にもデータベースを作成します。

  5. Heroオブジェクトを作成します。

    データベースにデータが既に作成されている場合は、バージョンにこれが含まれていない可能性があります。

  6. 新しい**session**を作成し、それを使用してヒーローをデータベースにaddし、変更をcommitします。

  7. データをクエリするために新しい**session**を作成します。

    !!! tip これは、上記の他の関数とは独立した新しい**session**であることに注意してください。

    But it still uses the same **engine**. We still have one engine for the whole application.
    
  8. select()関数を使用して、すべてのHeroオブジェクトを選択するステートメントを作成します。

    これは、heroテーブルのすべての行を選択します。

  9. session.exec(statement)を使用して、**session**に**engine**を使用して内部SQLステートメントを実行させます。

    これはデータベースにアクセスし、そのSQLを実行して、結果を取得します。

    これは、results 変数に格納する特別なイテラブルオブジェクトを返します。

    これは次の出力を生成します。

    INFO Engine BEGIN (implicit)
    INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
    FROM hero
    INFO Engine [no key 0.00032s] ()
    
  10. results 内の各 Hero オブジェクトに対して反復処理を行います。

  11. hero を出力します。

    for ループの3回の反復処理で、次の出力が生成されます。

    id=1 name='Deadpond' age=None secret_name='Dive Wilson'
    id=2 name='Spider-Boy' age=None secret_name='Pedro Parqueador'
    id=3 name='Rusty-Man' age=48 secret_name='Tommy Sharp'
    
  12. この時点で、with ブロックの後、**セッション**は閉じられます。

    これは次の出力を生成します。

    INFO Engine ROLLBACK
    
  13. この関数 select_heroes()main() 関数に追加して、コマンドラインからこのプログラムを実行したときに呼び出されるようにします。

from typing import Optional

from sqlmodel import Field, Session, SQLModel, create_engine, select  # (1)!


class Hero(SQLModel, table=True):  # (2)!
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: Optional[int] = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)  # (3)!


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)  # (4)!


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")  # (5)!
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    with Session(engine) as session:  # (6)!
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        session.commit()


def select_heroes():
    with Session(engine) as session:  # (7)!
        statement = select(Hero)  # (8)!
        results = session.exec(statement)  # (9)!
        for hero in results:  # (10)!
            print(hero)  # (11)!
    # (12)!


def main():
    create_db_and_tables()
    create_heroes()
    select_heroes()  # (13)!


if __name__ == "__main__":
    main()
  1. 新しいselect()関数を含む、使用するすべてのものをsqlmodelからインポートします。

  2. heroテーブルを表すHeroクラスモデルを作成します。

  3. **engine**を作成します。アプリケーションコード全体で共有される1つのエンジンを使用する必要があり、ここでそれを行っています。

  4. SQLModel.metadataに登録されているモデルのすべてのテーブルを作成します。

    これは、データベースがまだ存在しない場合にもデータベースを作成します。

  5. Heroオブジェクトを作成します。

    データベースにデータが既に作成されている場合は、バージョンにこれが含まれていない可能性があります。

  6. 新しい**session**を作成し、それを使用してヒーローをデータベースにaddし、変更をcommitします。

  7. データをクエリするために新しい**session**を作成します。

    !!! tip これは、上記の他の関数とは独立した新しい**session**であることに注意してください。

    But it still uses the same **engine**. We still have one engine for the whole application.
    
  8. select()関数を使用して、すべてのHeroオブジェクトを選択するステートメントを作成します。

    これは、heroテーブルのすべての行を選択します。

  9. session.exec(statement)を使用して、**session**に**engine**を使用して内部SQLステートメントを実行させます。

    これはデータベースにアクセスし、そのSQLを実行して、結果を取得します。

    これは、results 変数に格納する特別なイテラブルオブジェクトを返します。

    これは次の出力を生成します。

    INFO Engine BEGIN (implicit)
    INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
    FROM hero
    INFO Engine [no key 0.00032s] ()
    
  10. results 内の各 Hero オブジェクトに対して反復処理を行います。

  11. hero を出力します。

    for ループの3回の反復処理で、次の出力が生成されます。

    id=1 name='Deadpond' age=None secret_name='Dive Wilson'
    id=2 name='Spider-Boy' age=None secret_name='Pedro Parqueador'
    id=3 name='Rusty-Man' age=48 secret_name='Tommy Sharp'
    
  12. この時点で、with ブロックの後、**セッション**は閉じられます。

    これは次の出力を生成します。

    INFO Engine ROLLBACK
    
  13. この関数 select_heroes()main() 関数に追加して、コマンドラインからこのプログラムを実行したときに呼び出されるようにします。

ヒント

コードの各行で何が行われているかを確認するには、番号のバブルを確認してください。

ここでは、アプリケーション全体で単一の**エンジン**を使用する必要がある理由、そして各操作グループに対して異なる**セッション**を使用する必要がある理由がより明確になり始めています。

この新しく作成されたセッションは、*同じ* **エンジン**を使用しますが、新しい独立した**セッション**です。

上記のモデルを作成するコードは、例えば、Web APIリクエストを処理し、モデルを作成する関数に配置することができます。

そして、データベースからデータを読み取る2番目のセクションは、他のリクエストのための別の関数に配置することができます。

そのため、両方のセクションは**異なる場所**に配置することができ、独自のセッションが必要になります。

情報

公平を期すために言うと、この例では、すべてのコードが実際に同じ**セッション**を共有することができ、実際には2つのセッションを持つ必要はありません。

しかし、これにより、それらをどのように分離できるかを示し、アプリケーションごとに**1つのエンジン**、そして操作グループごとに**複数のセッション**を持つべきだという考えを強調することができます。

Hero オブジェクトのリストを取得する

これまでは、反復処理するために results を使用していました。

しかし、さまざまな理由で、*イテラブル* ではなく、**Hero の完全なリスト**をすぐに取得したい場合があります。たとえば、Web API でそれらを返す場合などです。

特別な results オブジェクトには、すべてのオブジェクトを含むリストを返すメソッド results.all() もあります。

# Code above omitted 👆

def select_heroes():
    with Session(engine) as session:
        statement = select(Hero)
        results = session.exec(statement)
        heroes = results.all()
        print(heroes)

# Code below omitted 👇
# Code above omitted 👆

def select_heroes():
    with Session(engine) as session:
        statement = select(Hero)
        results = session.exec(statement)
        heroes = results.all()
        print(heroes)

# Code below omitted 👇
👀 ファイル全体のプレビュー
from sqlmodel import Field, Session, SQLModel, create_engine, select


class Hero(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: int | None = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        session.commit()


def select_heroes():
    with Session(engine) as session:
        statement = select(Hero)
        results = session.exec(statement)
        heroes = results.all()
        print(heroes)


def main():
    create_db_and_tables()
    create_heroes()
    select_heroes()


if __name__ == "__main__":
    main()
from typing import Optional

from sqlmodel import Field, Session, SQLModel, create_engine, select


class Hero(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: Optional[int] = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        session.commit()


def select_heroes():
    with Session(engine) as session:
        statement = select(Hero)
        results = session.exec(statement)
        heroes = results.all()
        print(heroes)


def main():
    create_db_and_tables()
    create_heroes()
    select_heroes()


if __name__ == "__main__":
    main()

これで、heroes 変数にリスト内のすべてのヒーローが格納されました。

出力すると、次のようなものが表示されます。

[
    Hero(id=1, name='Deadpond', age=None, secret_name='Dive Wilson'),
    Hero(id=2, name='Spider-Boy', age=None, secret_name='Pedro Parqueador'),
    Hero(id=3, name='Rusty-Man', age=48, secret_name='Tommy Sharp')
]

情報

実際にはもっとコンパクトに見えますが、実際にはすべてのデータを含むリストであることがわかるように少しフォーマットしています。

コンパクト版

それぞれのものが何をしているのかを説明するために、いくつかの変数を作成してきました。

しかし、各オブジェクトが何であるか、そしてすべてが何をしているのかを知っていれば、それを少し簡略化して、よりコンパクトな形式にすることができます。

# Code above omitted 👆

def select_heroes():
    with Session(engine) as session:
        heroes = session.exec(select(Hero)).all()
        print(heroes)

# Code below omitted 👇
# Code above omitted 👆

def select_heroes():
    with Session(engine) as session:
        heroes = session.exec(select(Hero)).all()
        print(heroes)

# Code below omitted 👇
👀 ファイル全体のプレビュー
from sqlmodel import Field, Session, SQLModel, create_engine, select


class Hero(SQLModel, table=True):
    id: int | None = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: int | None = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        session.commit()


def select_heroes():
    with Session(engine) as session:
        heroes = session.exec(select(Hero)).all()
        print(heroes)


def main():
    create_db_and_tables()
    create_heroes()
    select_heroes()


if __name__ == "__main__":
    main()
from typing import Optional

from sqlmodel import Field, Session, SQLModel, create_engine, select


class Hero(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: Optional[int] = None


sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

engine = create_engine(sqlite_url, echo=True)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)


def create_heroes():
    hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
    hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")
    hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)

    with Session(engine) as session:
        session.add(hero_1)
        session.add(hero_2)
        session.add(hero_3)

        session.commit()


def select_heroes():
    with Session(engine) as session:
        heroes = session.exec(select(Hero)).all()
        print(heroes)


def main():
    create_db_and_tables()
    create_heroes()
    select_heroes()


if __name__ == "__main__":
    main()

ここでは、すべてを1行にまとめています。おそらく、select文をこのように1行にまとめることが多くなるでしょう。

SQLModel または SQLAlchemy - 技術的な詳細

**SQLModel** は、実際には、多かれ少なかれ、**SQLAlchemy** と **Pydantic** を組み合わせたものです。

同じタイプのオブジェクトを使用および返却し、両方のライブラリと互換性があります。

それにもかかわらず、**SQLModel** は開発者エクスペリエンスを向上させるために、独自の内部部品をいくつか定義しています。

この章では、それらのいくつかについて触れています。

SQLModel の select

sqlmodel から select() 関数をインポートする場合、**SQLModel** バージョンの select を使用しています。

SQLAchemy にも独自の select があり、SQLModel の select は内部で SQLAlchemy の select を使用します。

しかし、SQLModel のバージョンは、**VS Code**、**PyCharm**、またはその他のエディタを使用している場合でも、最高の**エディタサポート**を得られるように、型アノテーションを使用して多くの**トリック**を実行します。 ✨

情報

これを可能な限り改善するために、さまざまなバージョンの内部コードを使用して、多くの作業と調査が行われました。 🤓

SQLModel の session.exec

📢 これは特に注意すべき点です。

SQLAlchemy 自身の Session には、session.execute() メソッドがあります。 session.exec() メソッドはありません。

SQLAlchemy のチュートリアルを見ると、常に session.execute() が使用されています。

**SQLModel** 自身の Session は、SQLAlchemy の Session を直接継承し、この追加メソッド session.exec() を追加します.内部的には、同じ session.execute() を使用します.

しかし、session.exec()session() のトリックと組み合わせていくつかの**トリック**を実行し、select からデータを取得した後でも、**オートコンプリート**と**インラインエラー**を備えた最高の**エディタサポート**を提供します。 ✨

たとえば、SQLAlchemy では、ここに .scalars() を追加する必要があります。

heroes = session.execute(select(Hero)).scalars().all()

しかし、複数のものを選択する場合は、それを削除する必要があります(後で説明します)。

SQLModel の session.exec() はそれを処理してくれるので、.scalars() を追加する必要はありません。

これは SQLAlchemy が現在提供できないものです。通常の session.execute() は、レガシーなものも含め、他のいくつかのユースケースをサポートしているため、この機能をサポートするためのすべての内部型アノテーションとトリックを持つことができないためです.

その上、**SQLModel** の session.exec() は、記述するコードの量を減らし、可能な限り直感的にするためのトリックもいくつか実行します。

しかし、SQLModel の Session は、session.execute() にもアクセスできます。

ヒント

エディタは、session.exec()session.execute() の両方に対してオートコンプリートを提供します。

📢 最高のエディタサポートと開発者エクスペリエンスを得るために、**常に session.exec() を使用することを忘れないでください**。

**SQLModel** フレーバーの注意点

SQLModel は、狭い範囲の**非常に一般的なユースケース**で最高の**開発者エクスペリエンス**を提供するように設計されています。 ✨

必要に応じて、SQLAlchemy と直接組み合わせて、低レベルのより「純粋な」SQL 構文、特殊なパターン、さらにはレガシーなものも含め、SQLAlchemy の**すべての機能**を使用することができます。 🤓

しかし、**SQLModel** の設計(例:型アノテーション)は、ドキュメントで説明している方法で使用していることを前提としています。

これにより、可能な限り多くの**オートコンプリート**と**インラインエラー**を得ることができます。 🚀

しかし、これはまた、SQLAlchemy のより**特殊なパターン**で SQLModel を使用すると、エディタが*エラーがある*と表示する場合がありますが、実際にはコードは動作する可能性があることを意味します。

それがトレードオフです。 🤷

しかし、これらの特殊なパターンが必要な状況では、SQLAlchemy を SQLModel と直接組み合わせて使用​​することができます(同じモデルなどを使用)。