接続されたテーブルの作成¶
ここでは、異なるテーブルに格納された接続されたデータを扱います。
したがって、最初のステップは、複数のテーブルを作成し、それらを接続して、あるテーブルの各行が別のテーブルの別の行を参照できるようにすることです。
これまで、単一のhero
テーブルでヒーローを扱ってきました。ここで、team
テーブルを追加しましょう。
チームテーブルは次のようになります。
id | 名前 | 本部 |
---|---|---|
1 | プリベンターズ | シャープタワー |
2 | Zフォース | シスターマーガレットのバー |
これらを接続するために、team_id
を使用してIDで各チームを指す別の列をヒーローテーブルに追加します。
id | 名前 | 秘密の名前 | 年齢 | team_id ✨ |
---|---|---|---|---|
1 | デッドポンド | ダイブ・ウィルソン | null | 2 ✨ |
2 | スパイダーボーイ | ペドロ・パルケドール | null | 1 ✨ |
3 | ラスティマン | トミー・シャープ | 48 | 1 ✨ |
このようにして、hero
テーブルの各行はteam
テーブルの行を指すことができます。
一対多と多対一¶
ここでは、1つのチームが複数のヒーローを持つ可能性があるリレーションシップで、接続されたデータを作成しています。そのため、一般に一対多または多対一のリレーションシップと呼ばれます。
ヒーローから開始すると、多対一の部分を見ることができます。複数のヒーローが1つのチームに所属する可能性があります。
これはおそらく最も一般的なタイプのリレーションシップであるため、これから始めます。ただし、多対多および一対一のリレーションシップもあります。
コードでテーブルを作成¶
team
テーブルの作成¶
まず、コードでテーブルを作成することから始めましょう。
sqlmodel
から必要なものをインポートし、新しいTeam
モデルを作成します。
from sqlmodel import Field, SQLModel, create_engine
class Team(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str = Field(index=True)
headquarters: str
# Code below omitted 👇
from typing import Optional
from sqlmodel import Field, SQLModel, create_engine
class Team(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str = Field(index=True)
headquarters: str
# Code below omitted 👇
👀 ファイルの完全プレビュー
from sqlmodel import Field, SQLModel, create_engine
class Team(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str = Field(index=True)
headquarters: str
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str = Field(index=True)
secret_name: str
age: int | None = Field(default=None, index=True)
team_id: int | None = Field(default=None, foreign_key="team.id")
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 main():
create_db_and_tables()
if __name__ == "__main__":
main()
from typing import Optional
from sqlmodel import Field, SQLModel, create_engine
class Team(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str = Field(index=True)
headquarters: str
class Hero(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str = Field(index=True)
secret_name: str
age: Optional[int] = Field(default=None, index=True)
team_id: Optional[int] = Field(default=None, foreign_key="team.id")
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 main():
create_db_and_tables()
if __name__ == "__main__":
main()
これは、Hero
モデルで行ってきたことと非常によく似ています。
Team
モデルは、自動的に"team"
という名前のテーブルに格納され、次の列が含まれます。
id
、データベースによって自動的に生成される主キーname
、チームの名前- また、この列のインデックスを作成するようにSQLModelに指示します。
headquarters
、チームの本部
そして最後に、構成でテーブルとしてマークします。
新しいhero
テーブルの作成¶
次に、hero
テーブルを作成しましょう。
これはこれまで使用してきたモデルと同じですが、新しい列team_id
を追加するだけです。
from sqlmodel import Field, SQLModel, create_engine
class Team(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str = Field(index=True)
headquarters: str
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str = Field(index=True)
secret_name: str
age: int | None = Field(default=None, index=True)
team_id: int | None = Field(default=None, foreign_key="team.id")
# Code below omitted 👇
from typing import Optional
from sqlmodel import Field, SQLModel, create_engine
class Team(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str = Field(index=True)
headquarters: str
class Hero(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str = Field(index=True)
secret_name: str
age: Optional[int] = Field(default=None, index=True)
team_id: Optional[int] = Field(default=None, foreign_key="team.id")
# Code below omitted 👇
👀 ファイルの完全プレビュー
from sqlmodel import Field, SQLModel, create_engine
class Team(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str = Field(index=True)
headquarters: str
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str = Field(index=True)
secret_name: str
age: int | None = Field(default=None, index=True)
team_id: int | None = Field(default=None, foreign_key="team.id")
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 main():
create_db_and_tables()
if __name__ == "__main__":
main()
from typing import Optional
from sqlmodel import Field, SQLModel, create_engine
class Team(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str = Field(index=True)
headquarters: str
class Hero(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str = Field(index=True)
secret_name: str
age: Optional[int] = Field(default=None, index=True)
team_id: Optional[int] = Field(default=None, foreign_key="team.id")
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 main():
create_db_and_tables()
if __name__ == "__main__":
main()
そのほとんどは見覚えがあるはずです。
列の名前はteam_id
になります。これは整数であり、データベースではNULL
になる可能性があります(またはPythonではNone
)。これは、どのチームにも所属していないヒーローがいる可能性があるためです。
ヒーローを作成するときにteam_id=None
を明示的に渡す必要がないように、Field()
にデフォルトのNone
を追加します。
さて、ここが新しい部分です。
Field()
で、引数foreign_key="team.id"
を渡します。これにより、この列team_id
がテーブルteam
への外部キーであることをデータベースに伝えます。「外部キー」とは、この列に外部テーブルの行を識別するためのキーがあることを意味します。
この列team_id
の値は、team
テーブルのid
列にある行と同じ整数になります。それが2つのテーブルを接続するものです。
foreign_key
の値¶
foreign_key
は文字列であることに注意してください。
その中には、テーブルの名前、次にドット、そして列の名前があります。
これはデータベース内のテーブルの名前なので、モデルクラスTeam
(大文字のT
を使用)の名前ではなく、"team"
です。
カスタムテーブル名がある場合は、そのカスタムテーブル名を使用します。
情報
モデルのカスタムテーブル名の設定については、高度なユーザーガイドで学ぶことができます。
テーブルを作成¶
これで、以前と同じコードを追加して、エンジンを作成し、テーブルを作成する関数を作成できます。
# Code above omitted 👆
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)
# Code above omitted 👆
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)
👀 ファイルの完全プレビュー
from sqlmodel import Field, SQLModel, create_engine
class Team(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str = Field(index=True)
headquarters: str
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str = Field(index=True)
secret_name: str
age: int | None = Field(default=None, index=True)
team_id: int | None = Field(default=None, foreign_key="team.id")
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 main():
create_db_and_tables()
if __name__ == "__main__":
main()
from typing import Optional
from sqlmodel import Field, SQLModel, create_engine
class Team(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str = Field(index=True)
headquarters: str
class Hero(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str = Field(index=True)
secret_name: str
age: Optional[int] = Field(default=None, index=True)
team_id: Optional[int] = Field(default=None, foreign_key="team.id")
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 main():
create_db_and_tables()
if __name__ == "__main__":
main()
そして、以前と同様に、この関数を別の関数main()
から呼び出し、その関数main()
をファイルのメインブロックに追加します。
# Code above omitted 👆
def main():
create_db_and_tables()
if __name__ == "__main__":
main()
# Code above omitted 👆
def main():
create_db_and_tables()
if __name__ == "__main__":
main()
👀 ファイルの完全プレビュー
from sqlmodel import Field, SQLModel, create_engine
class Team(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str = Field(index=True)
headquarters: str
class Hero(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str = Field(index=True)
secret_name: str
age: int | None = Field(default=None, index=True)
team_id: int | None = Field(default=None, foreign_key="team.id")
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 main():
create_db_and_tables()
if __name__ == "__main__":
main()
from typing import Optional
from sqlmodel import Field, SQLModel, create_engine
class Team(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str = Field(index=True)
headquarters: str
class Hero(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str = Field(index=True)
secret_name: str
age: Optional[int] = Field(default=None, index=True)
team_id: Optional[int] = Field(default=None, foreign_key="team.id")
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 main():
create_db_and_tables()
if __name__ == "__main__":
main()
コードを実行¶
ヒント
コードを実行する前に、database.db
ファイルを削除して、最初からやり直すようにしてください。
ここまでのコードを実行すると、データベースファイルdatabase.db
と、定義したばかりのteam
とhero
テーブルが作成されます。
$ python app.py
// Automatically start a new transaction
INFO Engine BEGIN (implicit)
// Check if the tables exist already
INFO Engine PRAGMA main.table_info("team")
INFO Engine [raw sql] ()
INFO Engine PRAGMA temp.table_info("team")
INFO Engine [raw sql] ()
INFO Engine PRAGMA main.table_info("hero")
INFO Engine [raw sql] ()
INFO Engine PRAGMA temp.table_info("hero")
INFO Engine [raw sql] ()
// Create the tables
INFO Engine
CREATE TABLE team (
id INTEGER,
name VARCHAR NOT NULL,
headquarters VARCHAR NOT NULL,
PRIMARY KEY (id)
)
INFO Engine [no key 0.00010s] ()
INFO Engine
CREATE TABLE hero (
id INTEGER,
name VARCHAR NOT NULL,
secret_name VARCHAR NOT NULL,
age INTEGER,
team_id INTEGER,
PRIMARY KEY (id),
FOREIGN KEY(team_id) REFERENCES team (id)
)
INFO Engine [no key 0.00026s] ()
INFO Engine COMMIT
SQLでテーブルを作成¶
同じ生成されたSQLコードを見てみましょう。
前に見たように、これらのVARCHAR
列は、これらの実験で使用しているデータベースであるSQLiteではTEXT
に変換されます。
したがって、最初のSQLは次のように記述することもできます。
CREATE TABLE team (
id INTEGER,
name TEXT NOT NULL,
headquarters TEXT NOT NULL,
PRIMARY KEY (id)
)
そして、2番目のテーブルは次のように記述できます。
CREATE TABLE hero (
id INTEGER,
name TEXT NOT NULL,
secret_name TEXT NOT NULL,
age INTEGER,
team_id INTEGER,
PRIMARY KEY (id),
FOREIGN KEY(team_id) REFERENCES team (id)
)
唯一の新しいのはFOREIGN KEY
行です。ご覧のとおり、このテーブルのどの列が外部キーであるか(team_id
)、参照する別の(外部)テーブル(team
)、およびどの列が接続する行を定義するキーであるか(id
)をデータベースに伝えます。
DB Browser for SQLiteで自由に試してください。
まとめ¶
SQLModelを使用すると、ほとんどの場合、2つのテーブルを接続するために、別のテーブルと列を指す文字列を持つforeign_key
を持つField()
のフィールド(列)のみが必要です。
テーブルが作成および接続されたので、次の章でいくつかの行を作成しましょう。🚀