行の作成 - セッションの使用 - INSERT¶
データベースとテーブルが作成されたので、データの追加を開始できます。
テーブルの外観を改めて確認しましょう。これは追加するデータです。
id | name | secret_name | age |
---|---|---|---|
1 | Deadpond | Dive Wilson | null |
2 | Spider-Boy | Pedro Parqueador | null |
3 | Rusty-Man | Tommy Sharp | 48 |
テーブルとデータベースの作成¶
前の章から続けます。
これはデータベースとテーブルを作成したコードです。新しいものは何もありません。
from sqlmodel import Field, SQLModel, create_engine # (2)!
class Hero(SQLModel, table=True): # (3)!
id: int | None = Field(default=None, primary_key=True) # (4)!
name: str # (5)!
secret_name: str # (6)!
age: int | None = None # (7)!
sqlite_file_name = "database.db" # (8)!
sqlite_url = f"sqlite:///{sqlite_file_name}" # (9)!
engine = create_engine(sqlite_url, echo=True) # (10)!
def create_db_and_tables(): # (11)!
SQLModel.metadata.create_all(engine) # (12)!
# More code here later 👈
if __name__ == "__main__": # (13)!
create_db_and_tables() # (14)!
None
になりうるフィールドを宣言するために、typing
からOptional
をインポートします。sqlmodel
から必要なものをインポートします:Field
、SQLModel
、create_engine
。-
データベース内の
hero
テーブルを表すHero
モデルクラスを作成します。また、このクラスを
table=True
で**テーブルモデル**としてマークします。 -
id
フィールドの作成データベースが値を割り当てるまでは
None
になる可能性があるため、Optional
でアノテーションします。**主キー**であるため、
Field()
と引数primary_key=True
を使用します。 -
name
フィールドの作成。必須であるため、デフォルト値はなく、
Optional
ではありません。 -
secret_name
フィールドの作成。これも必須です。
-
age
フィールドの作成。必須ではなく、デフォルト値は
None
です。データベースでは、デフォルト値は
NULL
(None
のSQL相当)になります。このフィールドは
None
(およびデータベース内のNULL
)になる可能性があるため、Optional
でアノテーションします。 -
データベースファイルの名前を記述します。
- データベースファイルの名前を使用してデータベースURLを作成します。
-
URLを使用してエンジンを作成します。
これはまだデータベースを作成しません。この時点ではファイルやテーブルは作成されません。この特定のデータベースとの接続を処理し、SQLite(URLに基づく)に対する特定のサポートを提供する**エンジン**オブジェクトのみが作成されます。
-
副作用を作成するコードを関数に入れます。
この場合、データベースファイルとテーブルを作成する1行のみです。
-
SQLModel.metadata
に自動的に登録されたすべてのテーブルを作成します。 -
メインブロック、または「トップレベルスクリプト環境」を追加します。
そして、次のようにPythonで直接呼び出されたときに実行されるロジックを追加します。
$ python app.py // Execute all the stuff and show the output
…しかし、このモジュールから何かをインポートする場合(例:)、これは実行されません。
from app import Hero
-
このメインブロックで、データベースファイルとテーブルを作成する関数を呼び出します。
このようにして、
$ python app.py // Doing stuff ✨
…と呼び出すと、データベースファイルとテーブルが作成されます。
from typing import Optional # (1)!
from sqlmodel import Field, SQLModel, create_engine # (2)!
class Hero(SQLModel, table=True): # (3)!
id: Optional[int] = Field(default=None, primary_key=True) # (4)!
name: str # (5)!
secret_name: str # (6)!
age: Optional[int] = None # (7)!
sqlite_file_name = "database.db" # (8)!
sqlite_url = f"sqlite:///{sqlite_file_name}" # (9)!
engine = create_engine(sqlite_url, echo=True) # (10)!
def create_db_and_tables(): # (11)!
SQLModel.metadata.create_all(engine) # (12)!
# More code here later 👈
if __name__ == "__main__": # (13)!
create_db_and_tables() # (14)!
None
になりうるフィールドを宣言するために、typing
からOptional
をインポートします。sqlmodel
から必要なものをインポートします:Field
、SQLModel
、create_engine
。-
データベース内の
hero
テーブルを表すHero
モデルクラスを作成します。また、このクラスを
table=True
で**テーブルモデル**としてマークします。 -
id
フィールドの作成データベースが値を割り当てるまでは
None
になる可能性があるため、Optional
でアノテーションします。**主キー**であるため、
Field()
と引数primary_key=True
を使用します。 -
name
フィールドの作成。必須であるため、デフォルト値はなく、
Optional
ではありません。 -
secret_name
フィールドの作成。これも必須です。
-
age
フィールドの作成。必須ではなく、デフォルト値は
None
です。データベースでは、デフォルト値は
NULL
(None
のSQL相当)になります。このフィールドは
None
(およびデータベース内のNULL
)になる可能性があるため、Optional
でアノテーションします。 -
データベースファイルの名前を記述します。
- データベースファイルの名前を使用してデータベースURLを作成します。
-
URLを使用してエンジンを作成します。
これはまだデータベースを作成しません。この時点ではファイルやテーブルは作成されません。この特定のデータベースとの接続を処理し、SQLite(URLに基づく)に対する特定のサポートを提供する**エンジン**オブジェクトのみが作成されます。
-
副作用を作成するコードを関数に入れます。
この場合、データベースファイルとテーブルを作成する1行のみです。
-
SQLModel.metadata
に自動的に登録されたすべてのテーブルを作成します。 -
メインブロック、または「トップレベルスクリプト環境」を追加します。
そして、次のようにPythonで直接呼び出されたときに実行されるロジックを追加します。
$ python app.py // Execute all the stuff and show the output
…しかし、このモジュールから何かをインポートする場合(例:)、これは実行されません。
from app import Hero
-
このメインブロックで、データベースファイルとテーブルを作成する関数を呼び出します。
このようにして、
$ python app.py // Doing stuff ✨
…と呼び出すと、データベースファイルとテーブルが作成されます。
データベースとテーブルを作成できるようになったので、この時点から続き、同じファイルにさらにコードを追加してデータを作成します。
SQLによるデータの作成¶
Pythonコードを使用する前に、SQLを使用してデータを作成する方法を見てみましょう。
データベースにDeadpond
のレコード/行を挿入したいとします。
これは次のSQLコードで行えます。
INSERT INTO "hero" ("name", "secret_name")
VALUES ("Deadpond", "Dive Wilson");
それは、ほぼ
ねえSQLデータベース👋、テーブル
"hero"
に何かをINSERT
(レコード/行を作成)してください。これらの特定の列にいくつかの値を持つ行を挿入したいです。
"name"
"secret_name"
そして、これらの列に入れたい値は
"Deadpond"
"Dive Wilson"
SQLite用DB Explorerでの試行¶
**SQLite用DB Explorer**でそのSQL文を試すことができます。
Open Databaseをクリックして同じdatabase.db
ファイルを選択することで、既に作成した同じデータベースを開いていることを確認してください。
ヒント
hero
テーブルを含むdatabase.db
ファイルがない場合は、上のPythonプログラムを実行して再作成できます。👆
次に、Execute SQLタブに移動し、上記のSQLをコピーします。
次のようになります。
"すべて実行" ▶ボタンをクリックします。
次に、Browse Dataタブに移動すると、新しく作成されたレコード/行が表示されます。
データベース内のデータとコード内のデータ¶
プログラミング言語でデータベース(SQLまたはその他)を使用する場合、常にメモリにデータがあります。コードで作成するオブジェクトと変数、そしてデータベースにデータがあります。
私たちは常にデータベースからデータの一部を取得して、メモリ内の変数に入れています。
同様に、私たちは常にコードにデータを含む変数とオブジェクトを作成し、それをデータベースに保存したいので、何らかの方法で送信します。
場合によっては、メモリにデータを作成し、変更してからデータベースに保存することもあります。
コード内のロジックを使用して、データベースにデータを保存したくないと判断し、削除することもあります。🔥そして、私たちはデータベースとの送受信なしに、メモリ内のデータのみを処理しました。
**SQLModel**は、このやり取りをできるだけシンプルで直感的で、プログラミングに近くするために、(実際にはSQLAlchemyを介して)あらゆることを行います。✨
しかし、データがある可能性のある2つの場所(メモリ内またはデータベース内)の分割は常に存在します。そして、それを念頭に置いておくことが重要です。🤓
Pythonと**SQLModel**によるデータの作成¶
では、Pythonで同じ行を作成しましょう。
まず、database.db
ファイルを削除して、クリーンな状態から始めます。
メモリにデータを実行するPythonコードがあり、データベースは独立したシステム(外部SQLiteファイル、または外部データベースサーバー)であるため、2つの手順を実行する必要があります。
- Pythonでメモリ内にデータを作成する(変数内)
- データをデータベースに保存/送信する
モデルインスタンスの作成¶
最初のステップ、メモリ内にデータを作成することから始めましょう。
データベース内のhero
テーブルを表すHero
クラスを既に作成しました。
作成する各インスタンスは、データベース内の行のデータを表します。
したがって、最初のステップは、単にHero
のインスタンスを作成することです。
3人のヒーローのために、すぐに3つ作成します。
# Code above omitted 👆
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)
# More code here later 👇
# Code above omitted 👆
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)
# More code here later 👇
👀ファイル全体のプレビュー
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()
ヒント
このファイルの上にあるコード(省略されたコード)は、この章の上部にあるものと同じコードです。
Hero
モデルを作成するために前に使用したコードと同じです。
それを関数create_heroes()
に入れて、後で完成したら呼び出します。
対話的にコードを試している場合は、直接記述することもできます。
**セッション**の作成¶
これまで、データベースとのやり取りには**エンジン**のみを使用していました。
**エンジン**は、すべてのコードで共有し、データベースとの通信、接続の処理(PostgreSQLやMySQLなどのサーバーデータベースを使用する場合)などを担当する単一のオブジェクトです。
しかし、**SQLModel**を使用する場合、ほとんどの場合、その上に配置された別のツールである**セッション**を使用します。
アプリケーション全体で1つである**エンジン**とは対照的に、一緒に属するデータベースの一連の操作ごとに新しい**セッション**を作成します。
実際、**セッション**は**エンジン**を必要とし、使用します。
たとえば、Webアプリケーションがある場合、通常はリクエストごとに1つの**セッション**があります。
アプリケーションのすべてのコード(すべてのリクエストで共有)では同じ**エンジン**を再利用します。しかし、各リクエストに対して、新しい**セッション**を作成して使用します。そして、リクエストが完了したら、セッションを閉じます。
最初のステップは、Session
クラスをインポートすることです。
from sqlmodel import Field, Session, SQLModel, create_engine
# Code below omitted 👇
from typing import Optional
from sqlmodel import Field, Session, SQLModel, create_engine
# Code below omitted 👇
👀ファイル全体のプレビュー
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)
session = Session(engine)
session.add(hero_1)
session.add(hero_2)
session.add(hero_3)
session.commit()
session.close()
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)
session = Session(engine)
session.add(hero_1)
session.add(hero_2)
session.add(hero_3)
session.commit()
session.close()
def main():
create_db_and_tables()
create_heroes()
if __name__ == "__main__":
main()
次に、新しいセッションを作成できます。
# Code above omitted 👆
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)
session = Session(engine)
# More code here later 👇
# Code above omitted 👆
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)
session = Session(engine)
# More code here later 👇
👀ファイル全体のプレビュー
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)
session = Session(engine)
session.add(hero_1)
session.add(hero_2)
session.add(hero_3)
session.commit()
session.close()
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)
session = Session(engine)
session.add(hero_1)
session.add(hero_2)
session.add(hero_3)
session.commit()
session.close()
def main():
create_db_and_tables()
create_heroes()
if __name__ == "__main__":
main()
新しいSession
は、パラメータとしてengine
を取ります。そして、内部的にそのengineを使用します。
ヒント
後で、with
ブロックを使用してsessionを作成するより良い方法を見ていきます。
セッションへのモデルインスタンスの追加¶
いくつかのヒーローモデルインスタンス(メモリ内のいくつかのオブジェクト)とsessionができたので、次のステップはそれらをセッションに追加することです。
# Code above omitted 👆
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)
session = Session(engine)
session.add(hero_1)
session.add(hero_2)
session.add(hero_3)
# More code here later 👇
# Code above omitted 👆
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)
session = Session(engine)
session.add(hero_1)
session.add(hero_2)
session.add(hero_3)
# More code here later 👇
👀ファイル全体のプレビュー
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)
session = Session(engine)
session.add(hero_1)
session.add(hero_2)
session.add(hero_3)
session.commit()
session.close()
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)
session = Session(engine)
session.add(hero_1)
session.add(hero_2)
session.add(hero_3)
session.commit()
session.close()
def main():
create_db_and_tables()
create_heroes()
if __name__ == "__main__":
main()
この時点では、ヒーローはまだデータベースに保存されていません。
そして、これはengineから独立したsessionを持つことが意味を持つケースの1つです。
セッションは、後でデータベースに保存する必要があるすべてのオブジェクトをメモリに保持しています。
準備ができたら、これらの変更をコミットできます。すると、sessionは内部のengineを使用して、適切なSQLをデータベースに送信することですべてのデータを保存し、その方法ですべての行を作成します。すべて単一のバッチで行われます。
これにより、データベースとのやり取りがより効率的になります(その他にもメリットがあります)。
技術的な詳細
セッションは新しいトランザクションを作成し、そのトランザクション内ですべてのSQLコードを実行します。
これにより、データが単一のバッチで保存され、すべて成功するかすべて失敗するかを保証しますが、データベースを壊れた状態のままにはしません。
セッションの変更をコミット¶
ヒーローがsessionにあり、それらをすべてデータベースに保存する準備ができたので、変更をコミットできます。
# Code above omitted 👆
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)
session = Session(engine)
session.add(hero_1)
session.add(hero_2)
session.add(hero_3)
session.commit()
# More code here later 👇
# Code above omitted 👆
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)
session = Session(engine)
session.add(hero_1)
session.add(hero_2)
session.add(hero_3)
session.commit()
# More code here later 👇
👀ファイル全体のプレビュー
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)
session = Session(engine)
session.add(hero_1)
session.add(hero_2)
session.add(hero_3)
session.commit()
session.close()
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)
session = Session(engine)
session.add(hero_1)
session.add(hero_2)
session.add(hero_3)
session.commit()
session.close()
def main():
create_db_and_tables()
create_heroes()
if __name__ == "__main__":
main()
この行が実行されると、sessionはengineを使用して、対応するSQLを送信することでデータベース内のすべてのデータを保存します。
スクリプトとしてのヒーローの作成¶
ヒーローを作成する関数ができました。
Pythonで直接このプログラムを実行するときに、それを呼び出すようにするだけです。
既に次のようなmainブロックがありました。
if __name__ == "__main__":
create_db_and_tables()
そこに新しい関数を次のように追加できます。
if __name__ == "__main__":
create_db_and_tables()
create_heroes()
しかし、整理するために、代わりに独立したスクリプトとして呼び出されたときに実行されるべきすべてのコードを含む新しい関数main()
を作成し、そこに以前の関数create_db_and_tables()
と新しい関数create_heroes()
を追加しましょう。
# Code above omitted 👆
def main():
create_db_and_tables()
create_heroes()
# More code here later 👇
# Code above omitted 👆
def main():
create_db_and_tables()
create_heroes()
# More code here later 👇
👀ファイル全体のプレビュー
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()
そして、そのmainブロックからその単一のmain()
関数を呼び出すことができます。
# Code above omitted 👆
def main():
create_db_and_tables()
create_heroes()
if __name__ == "__main__":
main()
# Code above omitted 👆
def main():
create_db_and_tables()
create_heroes()
if __name__ == "__main__":
main()
👀ファイル全体のプレビュー
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()
スクリプトとして呼び出されたときに発生するすべてのことを単一の関数に入れることで、後で簡単にコードを追加できます。
そして、他のコードも必要であれば、この同じmain()
関数をインポートして使用することもできます。
スクリプトの実行¶
これで、コンソールからスクリプトとしてプログラムを実行できます。
echo=True
でengineを作成したので、実行しているすべてのSQLコードが出力されます。
$ python app.py
// Some boilerplate, checking that the hero table already exists
INFO Engine BEGIN (implicit)
INFO Engine PRAGMA main.table_info("hero")
INFO Engine [raw sql] ()
INFO Engine COMMIT
// BEGIN a transaction automatically ✨
INFO Engine BEGIN (implicit)
// Our INSERT statement, it uses VALUES (?, ?, ?) as parameters
INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
// ...and these are the parameter values 🚀
INFO Engine [generated in 0.00013s] ('Deadpond', 'Dive Wilson', None)
// Again, for Spider-Boy
INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
INFO Engine [cached since 0.000755s ago] ('Spider-Boy', 'Pedro Parqueador', None)
// And now for Rusty-Man
INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
INFO Engine [cached since 0.001014s ago] ('Rusty-Man', 'Tommy Sharp', 48)
// All good? Yes, commit this transaction! 🎉
INFO Engine COMMIT
Gitを使ったことがあるなら、これは非常に似たように機能します。
session.add()
を使用して、新しいオブジェクト(モデルインスタンス)をセッションに追加します(git add
に似ています)。
そして、それはまだ保存されていないが、保存の準備ができているデータのグループになります。
さらに変更を加えたり、オブジェクトを追加したりすることができます。
準備ができたら、一度にすべての変更をコミットできます(git commit
に似ています)。
セッションのクローズ¶
sessionは、engineからの接続などのリソースを保持しています。
そのため、セッションが終了したら、クローズしてこれらのリソースを解放し、クリーンアップを終了する必要があります。
# Code above omitted 👆
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)
session = Session(engine)
session.add(hero_1)
session.add(hero_2)
session.add(hero_3)
session.commit()
session.close()
# More code here later 👇
# Code above omitted 👆
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)
session = Session(engine)
session.add(hero_1)
session.add(hero_2)
session.add(hero_3)
session.commit()
session.close()
# More code here later 👇
👀ファイル全体のプレビュー
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)
session = Session(engine)
session.add(hero_1)
session.add(hero_2)
session.add(hero_3)
session.commit()
session.close()
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)
session = Session(engine)
session.add(hero_1)
session.add(hero_2)
session.add(hero_3)
session.commit()
session.close()
def main():
create_db_and_tables()
create_heroes()
if __name__ == "__main__":
main()
しかし、セッションのクローズを忘れた場合はどうなりますか?
または、コードで例外が発生し、session.close()
に到達しない場合はどうなりますか?
そのため、with
ブロックを使用してセッションを作成およびクローズするより良い方法があります。👇
with
ブロック内のセッション¶
Session
の動作方法、手動での作成方法、クローズ方法を知っておくことは有益です。たとえば、インタラクティブなセッション(Jupyterなど)でコードを調べたい場合に役立つ可能性があります。
しかし、セッションを処理するより良い方法として、with
ブロックを使用できます。
# Code above omitted 👆
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()
# Code above omitted 👆
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()
👀ファイル全体のプレビュー
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()
これは、セッションを手動で作成して手動でクローズするのと同じです。しかし、ここではwith
ブロックを使用することで、開始時に自動的に作成され、変数session
に割り当てられ、with
ブロックが終了した後、自動的にクローズされます。
そして、コードで例外が発生した場合でも動作します。😎
すべてのコードのレビュー¶
このファイル全体を最終的に見てみましょう。🔍
Hero
モデルクラス、engineの作成、データベースとテーブルの作成という最初の部分はすでにすべて知っています。
新しいコードに焦点を当てましょう。
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(): # (1)!
hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson") # (2)!
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: # (3)!
session.add(hero_1) # (4)!
session.add(hero_2)
session.add(hero_3)
session.commit() # (5)!
# (6)!
def main(): # (7)!
create_db_and_tables() # (8)!
create_heroes() # (9)!
if __name__ == "__main__": # (10)!
main() # (11)!
-
このロジックをまとめるために、関数
create_heroes()
を使用します。 -
Hero
モデルのオブジェクト/インスタンスをそれぞれ作成します。それぞれが1行のデータを表します。
-
with
ブロックを使用して、engine
を使用してSession
を作成します。新しいsessionは変数
session
に割り当てられます。そして、
with
ブロックが終了すると自動的にクローズされます。 -
各オブジェクト/インスタンスをsessionに追加します。
これらのオブジェクトはそれぞれデータベース内の1行を表します。
それらはすべて、保存されるのを待っているセッション内にあります。
-
データベースに変更をコミットします。
これにより、実際にデータがデータベースに送信されます。
トランザクションが自動的に開始され、すべてのデータが単一のバッチで保存されます。
-
with
ブロックが終了した時点で、sessionは自動的にクローズされます。 -
プログラムがコンソールからスクリプトとして呼び出されたときに実行されるすべてのコードを含む
main()
関数があります。そのため、後でこの関数にコードを追加できます。
次に、この
main()
関数を下のmainブロックに入れます。そして、単一の関数であるため、他のPythonファイルはそれをインポートして直接呼び出すことができます。
-
この
main()
関数では、データベースとテーブルも作成しています。以前のバージョンでは、この関数はmainブロックで直接呼び出されていました。
しかし、今は
main()
関数で呼び出されるだけです。 -
そして、今ではこの
main()
関数でヒーローも作成しています。 -
コマンドラインからスクリプトとしてプログラムを実行したときに実行するコードをいくつか含むmainブロックはまだあります。
$ python app.py // Do whatever is in the main block 🚀
-
コンソールからプログラムを実行するときに実行されるすべてのコードを含む単一の
main()
関数があります。そのため、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(): # (1)!
hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson") # (2)!
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: # (3)!
session.add(hero_1) # (4)!
session.add(hero_2)
session.add(hero_3)
session.commit() # (5)!
# (6)!
def main(): # (7)!
create_db_and_tables() # (8)!
create_heroes() # (9)!
if __name__ == "__main__": # (10)!
main() # (11)!
-
このロジックをまとめるために、関数
create_heroes()
を使用します。 -
Hero
モデルのオブジェクト/インスタンスをそれぞれ作成します。それぞれが1行のデータを表します。
-
with
ブロックを使用して、engine
を使用してSession
を作成します。新しいsessionは変数
session
に割り当てられます。そして、
with
ブロックが終了すると自動的にクローズされます。 -
各オブジェクト/インスタンスをsessionに追加します。
これらのオブジェクトはそれぞれデータベース内の1行を表します。
それらはすべて、保存されるのを待っているセッション内にあります。
-
データベースに変更をコミットします。
これにより、実際にデータがデータベースに送信されます。
トランザクションが自動的に開始され、すべてのデータが単一のバッチで保存されます。
-
with
ブロックが終了した時点で、sessionは自動的にクローズされます。 -
プログラムがコンソールからスクリプトとして呼び出されたときに実行されるすべてのコードを含む
main()
関数があります。そのため、後でこの関数にコードを追加できます。
次に、この
main()
関数を下のmainブロックに入れます。そして、単一の関数であるため、他のPythonファイルはそれをインポートして直接呼び出すことができます。
-
この
main()
関数では、データベースとテーブルも作成しています。以前のバージョンでは、この関数はmainブロックで直接呼び出されていました。
しかし、今は
main()
関数で呼び出されるだけです。 -
そして、今ではこの
main()
関数でヒーローも作成しています。 -
コマンドラインからスクリプトとしてプログラムを実行したときに実行するコードをいくつか含むmainブロックはまだあります。
$ python app.py // Do whatever is in the main block 🚀
-
コンソールからプログラムを実行するときに実行されるすべてのコードを含む単一の
main()
関数があります。そのため、mainブロックにこれだけのものがあれば十分です。
main()
関数を呼び出すだけです。
ヒント
コード内の各番号のバブルをクリックして、各行が何をするのかを確認してください。👆
これで、app.py
ファイルに入れてPythonで実行できます。そして、上記のような出力が表示されます。
その後、DB Browser for SQLiteでデータベースを開くと、データの参照タブに作成したデータが表示されます。
次のステップ¶
これで、データベースに行を追加する方法がわかりました。🎉
id
フィールドがデータベースではNULLにできない(プライマリキーだから)が、PythonコードではNoneにすることができる理由をよりよく理解するのに良い時期です。
次の章でそれについて説明します。🚀