データの読み取り - 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
ここでは、id
とname
列のみを選択しています。
そして、次のようなテーブルになります。
id | 名前 |
---|---|
1 | デッドポンド |
2 | スパイダーボーイ |
3 | ラスティマン |
ここで興味深いことに、SQLデータベースはデータをテーブルに格納します。そして、常に結果を**テーブル**で伝えます。
SELECT
のバリエーション¶
SQL言語では、いくつかの場所で**バリエーション**が許容されます。
そのバリエーションの1つは、SELECT
ステートメントで列名を直接使用するか、テーブル名とドットを前に付けて使用できることです。
例えば、上記のSQLコードは次のように書くこともできます。
SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
これは、後で同じ名前の列を持つ複数のテーブルを同時に操作する場合に特に重要になります。
例えば、hero.id
とteam.id
、またはhero.name
とteam.name
です。
もう1つのバリエーションは、SELECT
のようなほとんどのSQLキーワードは、select
のように小文字で書くこともできることです。
結果テーブルは存在する必要がない¶
これが興味深い部分です。SQLデータベースによって返されるテーブルは、データベースに独立したテーブルとして**存在する必要はありません**。 🧙
例えば、私たちのデータベースには、id
、name
、secret_name
、age
のすべての列を持つテーブルが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
クラスモデルHero
をselect()
関数に渡します。これは、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()
-
新しい
select()
関数を含む、使用するすべてのものをsqlmodel
からインポートします。 -
hero
テーブルを表すHero
クラスモデルを作成します。 -
**engine**を作成します。アプリケーションコード全体で共有される1つのエンジンを使用する必要があり、ここでそれを行っています。
-
SQLModel.metadata
に登録されているモデルのすべてのテーブルを作成します。これは、データベースがまだ存在しない場合にもデータベースを作成します。
-
各
Hero
オブジェクトを作成します。データベースにデータが既に作成されている場合は、バージョンにこれが含まれていない可能性があります。
-
新しい**session**を作成し、それを使用してヒーローをデータベースに
add
し、変更をcommit
します。 -
データをクエリするために新しい**session**を作成します。
!!! tip これは、上記の他の関数とは独立した新しい**session**であることに注意してください。
But it still uses the same **engine**. We still have one engine for the whole application.
-
select()
関数を使用して、すべてのHero
オブジェクトを選択するステートメントを作成します。これは、
hero
テーブルのすべての行を選択します。 -
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] ()
-
results
内の各Hero
オブジェクトに対して反復処理を行います。 -
各
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'
-
この時点で、
with
ブロックの後、**セッション**は閉じられます。これは次の出力を生成します。
INFO Engine ROLLBACK
-
この関数
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()
-
新しい
select()
関数を含む、使用するすべてのものをsqlmodel
からインポートします。 -
hero
テーブルを表すHero
クラスモデルを作成します。 -
**engine**を作成します。アプリケーションコード全体で共有される1つのエンジンを使用する必要があり、ここでそれを行っています。
-
SQLModel.metadata
に登録されているモデルのすべてのテーブルを作成します。これは、データベースがまだ存在しない場合にもデータベースを作成します。
-
各
Hero
オブジェクトを作成します。データベースにデータが既に作成されている場合は、バージョンにこれが含まれていない可能性があります。
-
新しい**session**を作成し、それを使用してヒーローをデータベースに
add
し、変更をcommit
します。 -
データをクエリするために新しい**session**を作成します。
!!! tip これは、上記の他の関数とは独立した新しい**session**であることに注意してください。
But it still uses the same **engine**. We still have one engine for the whole application.
-
select()
関数を使用して、すべてのHero
オブジェクトを選択するステートメントを作成します。これは、
hero
テーブルのすべての行を選択します。 -
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] ()
-
results
内の各Hero
オブジェクトに対して反復処理を行います。 -
各
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'
-
この時点で、
with
ブロックの後、**セッション**は閉じられます。これは次の出力を生成します。
INFO Engine ROLLBACK
-
この関数
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 と直接組み合わせて使用することができます(同じモデルなどを使用)。