From: Sebastián Ramírez Date: Wed, 29 Nov 2023 15:51:55 +0000 (+0100) Subject: 📝 Add source examples for Python 3.9 and 3.10 (#715) X-Git-Tag: 0.0.13~14 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d8effcbc5cef783063d59e3cc3dd6e7c492ca4c4;p=thirdparty%2Ffastapi%2Fsqlmodel.git 📝 Add source examples for Python 3.9 and 3.10 (#715) * 📝 Add source examples for Python 3.9 and 3.10 * ✅ Add tests for new source examples for Python 3.9 and 3.10, still needs pytest markers * ✅ Add tests for fastapi examples * ✅ Update tests for FastAPI app testing, for Python 3.9 and 3.10, fixing multi-app testing conflicts * ✅ Require Python 3.9 and 3.10 for tests * ✅ Update tests with missing markers --- diff --git a/docs_src/advanced/decimal/tutorial001_py310.py b/docs_src/advanced/decimal/tutorial001_py310.py new file mode 100644 index 00000000..92afc090 --- /dev/null +++ b/docs_src/advanced/decimal/tutorial001_py310.py @@ -0,0 +1,59 @@ +from pydantic import condecimal +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 = Field(index=True) + secret_name: str + age: int | None = Field(default=None, index=True) + money: condecimal(max_digits=5, decimal_places=3) = Field(default=0) + + +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", money=1.1) + hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador", money=0.001) + hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48, money=2.2) + + 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).where(Hero.name == "Deadpond") + results = session.exec(statement) + hero_1 = results.one() + print("Hero 1:", hero_1) + + statement = select(Hero).where(Hero.name == "Rusty-Man") + results = session.exec(statement) + hero_2 = results.one() + print("Hero 2:", hero_2) + + total_money = hero_1.money + hero_2.money + print(f"Total money: {total_money}") + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/automatic_id_none_refresh/tutorial001_py310.py b/docs_src/tutorial/automatic_id_none_refresh/tutorial001_py310.py new file mode 100644 index 00000000..6b76da58 --- /dev/null +++ b/docs_src/tutorial/automatic_id_none_refresh/tutorial001_py310.py @@ -0,0 +1,79 @@ +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) + + print("Before interacting with the database") + print("Hero 1:", hero_1) + print("Hero 2:", hero_2) + print("Hero 3:", hero_3) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + + print("After adding to the session") + print("Hero 1:", hero_1) + print("Hero 2:", hero_2) + print("Hero 3:", hero_3) + + session.commit() + + print("After committing the session") + print("Hero 1:", hero_1) + print("Hero 2:", hero_2) + print("Hero 3:", hero_3) + + print("After committing the session, show IDs") + print("Hero 1 ID:", hero_1.id) + print("Hero 2 ID:", hero_2.id) + print("Hero 3 ID:", hero_3.id) + + print("After committing the session, show names") + print("Hero 1 name:", hero_1.name) + print("Hero 2 name:", hero_2.name) + print("Hero 3 name:", hero_3.name) + + session.refresh(hero_1) + session.refresh(hero_2) + session.refresh(hero_3) + + print("After refreshing the heroes") + print("Hero 1:", hero_1) + print("Hero 2:", hero_2) + print("Hero 3:", hero_3) + + print("After the session closes") + print("Hero 1:", hero_1) + print("Hero 2:", hero_2) + print("Hero 3:", hero_3) + + +def main(): + create_db_and_tables() + create_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/automatic_id_none_refresh/tutorial002_py310.py b/docs_src/tutorial/automatic_id_none_refresh/tutorial002_py310.py new file mode 100644 index 00000000..0f3ad44c --- /dev/null +++ b/docs_src/tutorial/automatic_id_none_refresh/tutorial002_py310.py @@ -0,0 +1,80 @@ +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") # (1)! + hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador") # (2)! + hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48) # (3)! + + print("Before interacting with the database") # (4)! + print("Hero 1:", hero_1) # (5)! + print("Hero 2:", hero_2) # (6)! + print("Hero 3:", hero_3) # (7)! + + with Session(engine) as session: # (8)! + session.add(hero_1) # (9)! + session.add(hero_2) # (10)! + session.add(hero_3) # (11)! + + print("After adding to the session") # (12)! + print("Hero 1:", hero_1) # (13)! + print("Hero 2:", hero_2) # (14)! + print("Hero 3:", hero_3) # (15)! + + session.commit() # (16)! + + print("After committing the session") # (17)! + print("Hero 1:", hero_1) # (18)! + print("Hero 2:", hero_2) # (19)! + print("Hero 3:", hero_3) # (20)! + + print("After committing the session, show IDs") # (21)! + print("Hero 1 ID:", hero_1.id) # (22)! + print("Hero 2 ID:", hero_2.id) # (23)! + print("Hero 3 ID:", hero_3.id) # (24)! + + print("After committing the session, show names") # (25)! + print("Hero 1 name:", hero_1.name) # (26)! + print("Hero 2 name:", hero_2.name) # (27)! + print("Hero 3 name:", hero_3.name) # (28)! + + session.refresh(hero_1) # (29)! + session.refresh(hero_2) # (30)! + session.refresh(hero_3) # (31)! + + print("After refreshing the heroes") # (32)! + print("Hero 1:", hero_1) # (33)! + print("Hero 2:", hero_2) # (34)! + print("Hero 3:", hero_3) # (35)! + # (36)! + + print("After the session closes") # (37)! + print("Hero 1:", hero_1) # (38)! + print("Hero 2:", hero_2) # (39)! + print("Hero 3:", hero_3) # (40)! + + +def main(): + create_db_and_tables() + create_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/code_structure/tutorial001_py310/__init__.py b/docs_src/tutorial/code_structure/tutorial001_py310/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/docs_src/tutorial/code_structure/tutorial001_py310/app.py b/docs_src/tutorial/code_structure/tutorial001_py310/app.py new file mode 100644 index 00000000..065f8a78 --- /dev/null +++ b/docs_src/tutorial/code_structure/tutorial001_py310/app.py @@ -0,0 +1,29 @@ +from sqlmodel import Session + +from .database import create_db_and_tables, engine +from .models import Hero, Team + + +def create_heroes(): + with Session(engine) as session: + team_z_force = Team(name="Z-Force", headquarters="Sister Margaret’s Bar") + + hero_deadpond = Hero( + name="Deadpond", secret_name="Dive Wilson", team=team_z_force + ) + session.add(hero_deadpond) + session.commit() + + session.refresh(hero_deadpond) + + print("Created hero:", hero_deadpond) + print("Hero's team:", hero_deadpond.team) + + +def main(): + create_db_and_tables() + create_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/code_structure/tutorial001_py310/database.py b/docs_src/tutorial/code_structure/tutorial001_py310/database.py new file mode 100644 index 00000000..d6de16c1 --- /dev/null +++ b/docs_src/tutorial/code_structure/tutorial001_py310/database.py @@ -0,0 +1,10 @@ +from sqlmodel import SQLModel, create_engine + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +engine = create_engine(sqlite_url) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) diff --git a/docs_src/tutorial/code_structure/tutorial001_py310/models.py b/docs_src/tutorial/code_structure/tutorial001_py310/models.py new file mode 100644 index 00000000..1f485ef0 --- /dev/null +++ b/docs_src/tutorial/code_structure/tutorial001_py310/models.py @@ -0,0 +1,19 @@ +from sqlmodel import Field, Relationship, SQLModel + + +class Team(SQLModel, table=True): + id: int | None = Field(default=None, primary_key=True) + name: str = Field(index=True) + headquarters: str + + heroes: list["Hero"] = Relationship(back_populates="team") + + +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") + team: Team | None = Relationship(back_populates="heroes") diff --git a/docs_src/tutorial/code_structure/tutorial001_py39/__init__.py b/docs_src/tutorial/code_structure/tutorial001_py39/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/docs_src/tutorial/code_structure/tutorial001_py39/app.py b/docs_src/tutorial/code_structure/tutorial001_py39/app.py new file mode 100644 index 00000000..065f8a78 --- /dev/null +++ b/docs_src/tutorial/code_structure/tutorial001_py39/app.py @@ -0,0 +1,29 @@ +from sqlmodel import Session + +from .database import create_db_and_tables, engine +from .models import Hero, Team + + +def create_heroes(): + with Session(engine) as session: + team_z_force = Team(name="Z-Force", headquarters="Sister Margaret’s Bar") + + hero_deadpond = Hero( + name="Deadpond", secret_name="Dive Wilson", team=team_z_force + ) + session.add(hero_deadpond) + session.commit() + + session.refresh(hero_deadpond) + + print("Created hero:", hero_deadpond) + print("Hero's team:", hero_deadpond.team) + + +def main(): + create_db_and_tables() + create_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/code_structure/tutorial001_py39/database.py b/docs_src/tutorial/code_structure/tutorial001_py39/database.py new file mode 100644 index 00000000..d6de16c1 --- /dev/null +++ b/docs_src/tutorial/code_structure/tutorial001_py39/database.py @@ -0,0 +1,10 @@ +from sqlmodel import SQLModel, create_engine + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +engine = create_engine(sqlite_url) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) diff --git a/docs_src/tutorial/code_structure/tutorial001_py39/models.py b/docs_src/tutorial/code_structure/tutorial001_py39/models.py new file mode 100644 index 00000000..ff6b6c2d --- /dev/null +++ b/docs_src/tutorial/code_structure/tutorial001_py39/models.py @@ -0,0 +1,21 @@ +from typing import Optional + +from sqlmodel import Field, Relationship, SQLModel + + +class Team(SQLModel, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + name: str = Field(index=True) + headquarters: str + + heroes: list["Hero"] = Relationship(back_populates="team") + + +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") + team: Optional[Team] = Relationship(back_populates="heroes") diff --git a/docs_src/tutorial/code_structure/tutorial002_py310/__init__.py b/docs_src/tutorial/code_structure/tutorial002_py310/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/docs_src/tutorial/code_structure/tutorial002_py310/app.py b/docs_src/tutorial/code_structure/tutorial002_py310/app.py new file mode 100644 index 00000000..8afaee7c --- /dev/null +++ b/docs_src/tutorial/code_structure/tutorial002_py310/app.py @@ -0,0 +1,30 @@ +from sqlmodel import Session + +from .database import create_db_and_tables, engine +from .hero_model import Hero +from .team_model import Team + + +def create_heroes(): + with Session(engine) as session: + team_z_force = Team(name="Z-Force", headquarters="Sister Margaret’s Bar") + + hero_deadpond = Hero( + name="Deadpond", secret_name="Dive Wilson", team=team_z_force + ) + session.add(hero_deadpond) + session.commit() + + session.refresh(hero_deadpond) + + print("Created hero:", hero_deadpond) + print("Hero's team:", hero_deadpond.team) + + +def main(): + create_db_and_tables() + create_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/code_structure/tutorial002_py310/database.py b/docs_src/tutorial/code_structure/tutorial002_py310/database.py new file mode 100644 index 00000000..d6de16c1 --- /dev/null +++ b/docs_src/tutorial/code_structure/tutorial002_py310/database.py @@ -0,0 +1,10 @@ +from sqlmodel import SQLModel, create_engine + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +engine = create_engine(sqlite_url) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) diff --git a/docs_src/tutorial/code_structure/tutorial002_py310/hero_model.py b/docs_src/tutorial/code_structure/tutorial002_py310/hero_model.py new file mode 100644 index 00000000..52fe68be --- /dev/null +++ b/docs_src/tutorial/code_structure/tutorial002_py310/hero_model.py @@ -0,0 +1,16 @@ +from typing import TYPE_CHECKING, Optional + +from sqlmodel import Field, Relationship, SQLModel + +if TYPE_CHECKING: + from .team_model import Team + + +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") + team: Optional["Team"] = Relationship(back_populates="heroes") diff --git a/docs_src/tutorial/code_structure/tutorial002_py310/team_model.py b/docs_src/tutorial/code_structure/tutorial002_py310/team_model.py new file mode 100644 index 00000000..10af5b9c --- /dev/null +++ b/docs_src/tutorial/code_structure/tutorial002_py310/team_model.py @@ -0,0 +1,14 @@ +from typing import TYPE_CHECKING + +from sqlmodel import Field, Relationship, SQLModel + +if TYPE_CHECKING: + from .hero_model import Hero + + +class Team(SQLModel, table=True): + id: int | None = Field(default=None, primary_key=True) + name: str = Field(index=True) + headquarters: str + + heroes: list["Hero"] = Relationship(back_populates="team") diff --git a/docs_src/tutorial/code_structure/tutorial002_py39/__init__.py b/docs_src/tutorial/code_structure/tutorial002_py39/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/docs_src/tutorial/code_structure/tutorial002_py39/app.py b/docs_src/tutorial/code_structure/tutorial002_py39/app.py new file mode 100644 index 00000000..8afaee7c --- /dev/null +++ b/docs_src/tutorial/code_structure/tutorial002_py39/app.py @@ -0,0 +1,30 @@ +from sqlmodel import Session + +from .database import create_db_and_tables, engine +from .hero_model import Hero +from .team_model import Team + + +def create_heroes(): + with Session(engine) as session: + team_z_force = Team(name="Z-Force", headquarters="Sister Margaret’s Bar") + + hero_deadpond = Hero( + name="Deadpond", secret_name="Dive Wilson", team=team_z_force + ) + session.add(hero_deadpond) + session.commit() + + session.refresh(hero_deadpond) + + print("Created hero:", hero_deadpond) + print("Hero's team:", hero_deadpond.team) + + +def main(): + create_db_and_tables() + create_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/code_structure/tutorial002_py39/database.py b/docs_src/tutorial/code_structure/tutorial002_py39/database.py new file mode 100644 index 00000000..d6de16c1 --- /dev/null +++ b/docs_src/tutorial/code_structure/tutorial002_py39/database.py @@ -0,0 +1,10 @@ +from sqlmodel import SQLModel, create_engine + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +engine = create_engine(sqlite_url) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) diff --git a/docs_src/tutorial/code_structure/tutorial002_py39/hero_model.py b/docs_src/tutorial/code_structure/tutorial002_py39/hero_model.py new file mode 100644 index 00000000..06dd9c6d --- /dev/null +++ b/docs_src/tutorial/code_structure/tutorial002_py39/hero_model.py @@ -0,0 +1,16 @@ +from typing import TYPE_CHECKING, Optional + +from sqlmodel import Field, Relationship, SQLModel + +if TYPE_CHECKING: + from .team_model import Team + + +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") + team: Optional["Team"] = Relationship(back_populates="heroes") diff --git a/docs_src/tutorial/code_structure/tutorial002_py39/team_model.py b/docs_src/tutorial/code_structure/tutorial002_py39/team_model.py new file mode 100644 index 00000000..b51c070c --- /dev/null +++ b/docs_src/tutorial/code_structure/tutorial002_py39/team_model.py @@ -0,0 +1,14 @@ +from typing import TYPE_CHECKING, Optional + +from sqlmodel import Field, Relationship, SQLModel + +if TYPE_CHECKING: + from .hero_model import Hero + + +class Team(SQLModel, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + name: str = Field(index=True) + headquarters: str + + heroes: list["Hero"] = Relationship(back_populates="team") diff --git a/docs_src/tutorial/connect/create_tables/tutorial001_py310.py b/docs_src/tutorial/connect/create_tables/tutorial001_py310.py new file mode 100644 index 00000000..460b9768 --- /dev/null +++ b/docs_src/tutorial/connect/create_tables/tutorial001_py310.py @@ -0,0 +1,34 @@ +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() diff --git a/docs_src/tutorial/connect/delete/tutorial001_py310.py b/docs_src/tutorial/connect/delete/tutorial001_py310.py new file mode 100644 index 00000000..4815ad4e --- /dev/null +++ b/docs_src/tutorial/connect/delete/tutorial001_py310.py @@ -0,0 +1,79 @@ +from sqlmodel import Field, Session, 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 create_heroes(): + with Session(engine) as session: + team_preventers = Team(name="Preventers", headquarters="Sharp Tower") + team_z_force = Team(name="Z-Force", headquarters="Sister Margaret’s Bar") + session.add(team_preventers) + session.add(team_z_force) + session.commit() + + hero_deadpond = Hero( + name="Deadpond", secret_name="Dive Wilson", team_id=team_z_force.id + ) + hero_rusty_man = Hero( + name="Rusty-Man", + secret_name="Tommy Sharp", + age=48, + team_id=team_preventers.id, + ) + hero_spider_boy = Hero(name="Spider-Boy", secret_name="Pedro Parqueador") + session.add(hero_deadpond) + session.add(hero_rusty_man) + session.add(hero_spider_boy) + session.commit() + + session.refresh(hero_deadpond) + session.refresh(hero_rusty_man) + session.refresh(hero_spider_boy) + + print("Created hero:", hero_deadpond) + print("Created hero:", hero_rusty_man) + print("Created hero:", hero_spider_boy) + + hero_spider_boy.team_id = team_preventers.id + session.add(hero_spider_boy) + session.commit() + session.refresh(hero_spider_boy) + print("Updated hero:", hero_spider_boy) + + hero_spider_boy.team_id = None + session.add(hero_spider_boy) + session.commit() + session.refresh(hero_spider_boy) + print("No longer Preventer:", hero_spider_boy) + + +def main(): + create_db_and_tables() + create_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/connect/insert/tutorial001_py310.py b/docs_src/tutorial/connect/insert/tutorial001_py310.py new file mode 100644 index 00000000..506429dc --- /dev/null +++ b/docs_src/tutorial/connect/insert/tutorial001_py310.py @@ -0,0 +1,67 @@ +from sqlmodel import Field, Session, 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 create_heroes(): + with Session(engine) as session: + team_preventers = Team(name="Preventers", headquarters="Sharp Tower") + team_z_force = Team(name="Z-Force", headquarters="Sister Margaret’s Bar") + session.add(team_preventers) + session.add(team_z_force) + session.commit() + + hero_deadpond = Hero( + name="Deadpond", secret_name="Dive Wilson", team_id=team_z_force.id + ) + hero_rusty_man = Hero( + name="Rusty-Man", + secret_name="Tommy Sharp", + age=48, + team_id=team_preventers.id, + ) + hero_spider_boy = Hero(name="Spider-Boy", secret_name="Pedro Parqueador") + session.add(hero_deadpond) + session.add(hero_rusty_man) + session.add(hero_spider_boy) + session.commit() + + session.refresh(hero_deadpond) + session.refresh(hero_rusty_man) + session.refresh(hero_spider_boy) + + print("Created hero:", hero_deadpond) + print("Created hero:", hero_rusty_man) + print("Created hero:", hero_spider_boy) + + +def main(): + create_db_and_tables() + create_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/connect/select/tutorial001_py310.py b/docs_src/tutorial/connect/select/tutorial001_py310.py new file mode 100644 index 00000000..c39894fc --- /dev/null +++ b/docs_src/tutorial/connect/select/tutorial001_py310.py @@ -0,0 +1,76 @@ +from sqlmodel import Field, Session, SQLModel, create_engine, select + + +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 create_heroes(): + with Session(engine) as session: + team_preventers = Team(name="Preventers", headquarters="Sharp Tower") + team_z_force = Team(name="Z-Force", headquarters="Sister Margaret’s Bar") + session.add(team_preventers) + session.add(team_z_force) + session.commit() + + hero_deadpond = Hero( + name="Deadpond", secret_name="Dive Wilson", team_id=team_z_force.id + ) + hero_rusty_man = Hero( + name="Rusty-Man", + secret_name="Tommy Sharp", + age=48, + team_id=team_preventers.id, + ) + hero_spider_boy = Hero(name="Spider-Boy", secret_name="Pedro Parqueador") + session.add(hero_deadpond) + session.add(hero_rusty_man) + session.add(hero_spider_boy) + session.commit() + + session.refresh(hero_deadpond) + session.refresh(hero_rusty_man) + session.refresh(hero_spider_boy) + + print("Created hero:", hero_deadpond) + print("Created hero:", hero_rusty_man) + print("Created hero:", hero_spider_boy) + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero, Team).where(Hero.team_id == Team.id) + results = session.exec(statement) + for hero, team in results: + print("Hero:", hero, "Team:", team) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/connect/select/tutorial002_py310.py b/docs_src/tutorial/connect/select/tutorial002_py310.py new file mode 100644 index 00000000..756dab09 --- /dev/null +++ b/docs_src/tutorial/connect/select/tutorial002_py310.py @@ -0,0 +1,76 @@ +from sqlmodel import Field, Session, SQLModel, create_engine, select + + +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 create_heroes(): + with Session(engine) as session: + team_preventers = Team(name="Preventers", headquarters="Sharp Tower") + team_z_force = Team(name="Z-Force", headquarters="Sister Margaret’s Bar") + session.add(team_preventers) + session.add(team_z_force) + session.commit() + + hero_deadpond = Hero( + name="Deadpond", secret_name="Dive Wilson", team_id=team_z_force.id + ) + hero_rusty_man = Hero( + name="Rusty-Man", + secret_name="Tommy Sharp", + age=48, + team_id=team_preventers.id, + ) + hero_spider_boy = Hero(name="Spider-Boy", secret_name="Pedro Parqueador") + session.add(hero_deadpond) + session.add(hero_rusty_man) + session.add(hero_spider_boy) + session.commit() + + session.refresh(hero_deadpond) + session.refresh(hero_rusty_man) + session.refresh(hero_spider_boy) + + print("Created hero:", hero_deadpond) + print("Created hero:", hero_rusty_man) + print("Created hero:", hero_spider_boy) + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero, Team).join(Team) + results = session.exec(statement) + for hero, team in results: + print("Hero:", hero, "Team:", team) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/connect/select/tutorial003_py310.py b/docs_src/tutorial/connect/select/tutorial003_py310.py new file mode 100644 index 00000000..63f5b273 --- /dev/null +++ b/docs_src/tutorial/connect/select/tutorial003_py310.py @@ -0,0 +1,76 @@ +from sqlmodel import Field, Session, SQLModel, create_engine, select + + +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 create_heroes(): + with Session(engine) as session: + team_preventers = Team(name="Preventers", headquarters="Sharp Tower") + team_z_force = Team(name="Z-Force", headquarters="Sister Margaret’s Bar") + session.add(team_preventers) + session.add(team_z_force) + session.commit() + + hero_deadpond = Hero( + name="Deadpond", secret_name="Dive Wilson", team_id=team_z_force.id + ) + hero_rusty_man = Hero( + name="Rusty-Man", + secret_name="Tommy Sharp", + age=48, + team_id=team_preventers.id, + ) + hero_spider_boy = Hero(name="Spider-Boy", secret_name="Pedro Parqueador") + session.add(hero_deadpond) + session.add(hero_rusty_man) + session.add(hero_spider_boy) + session.commit() + + session.refresh(hero_deadpond) + session.refresh(hero_rusty_man) + session.refresh(hero_spider_boy) + + print("Created hero:", hero_deadpond) + print("Created hero:", hero_rusty_man) + print("Created hero:", hero_spider_boy) + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero, Team).join(Team, isouter=True) + results = session.exec(statement) + for hero, team in results: + print("Hero:", hero, "Team:", team) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/connect/select/tutorial004_py310.py b/docs_src/tutorial/connect/select/tutorial004_py310.py new file mode 100644 index 00000000..8b024321 --- /dev/null +++ b/docs_src/tutorial/connect/select/tutorial004_py310.py @@ -0,0 +1,76 @@ +from sqlmodel import Field, Session, SQLModel, create_engine, select + + +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 create_heroes(): + with Session(engine) as session: + team_preventers = Team(name="Preventers", headquarters="Sharp Tower") + team_z_force = Team(name="Z-Force", headquarters="Sister Margaret’s Bar") + session.add(team_preventers) + session.add(team_z_force) + session.commit() + + hero_deadpond = Hero( + name="Deadpond", secret_name="Dive Wilson", team_id=team_z_force.id + ) + hero_rusty_man = Hero( + name="Rusty-Man", + secret_name="Tommy Sharp", + age=48, + team_id=team_preventers.id, + ) + hero_spider_boy = Hero(name="Spider-Boy", secret_name="Pedro Parqueador") + session.add(hero_deadpond) + session.add(hero_rusty_man) + session.add(hero_spider_boy) + session.commit() + + session.refresh(hero_deadpond) + session.refresh(hero_rusty_man) + session.refresh(hero_spider_boy) + + print("Created hero:", hero_deadpond) + print("Created hero:", hero_rusty_man) + print("Created hero:", hero_spider_boy) + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero).join(Team).where(Team.name == "Preventers") + results = session.exec(statement) + for hero in results: + print("Preventer Hero:", hero) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/connect/select/tutorial005_py310.py b/docs_src/tutorial/connect/select/tutorial005_py310.py new file mode 100644 index 00000000..2120640b --- /dev/null +++ b/docs_src/tutorial/connect/select/tutorial005_py310.py @@ -0,0 +1,76 @@ +from sqlmodel import Field, Session, SQLModel, create_engine, select + + +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 create_heroes(): + with Session(engine) as session: + team_preventers = Team(name="Preventers", headquarters="Sharp Tower") + team_z_force = Team(name="Z-Force", headquarters="Sister Margaret’s Bar") + session.add(team_preventers) + session.add(team_z_force) + session.commit() + + hero_deadpond = Hero( + name="Deadpond", secret_name="Dive Wilson", team_id=team_z_force.id + ) + hero_rusty_man = Hero( + name="Rusty-Man", + secret_name="Tommy Sharp", + age=48, + team_id=team_preventers.id, + ) + hero_spider_boy = Hero(name="Spider-Boy", secret_name="Pedro Parqueador") + session.add(hero_deadpond) + session.add(hero_rusty_man) + session.add(hero_spider_boy) + session.commit() + + session.refresh(hero_deadpond) + session.refresh(hero_rusty_man) + session.refresh(hero_spider_boy) + + print("Created hero:", hero_deadpond) + print("Created hero:", hero_rusty_man) + print("Created hero:", hero_spider_boy) + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero, Team).join(Team).where(Team.name == "Preventers") + results = session.exec(statement) + for hero, team in results: + print("Preventer Hero:", hero, "Team:", team) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/connect/update/tutorial001_py310.py b/docs_src/tutorial/connect/update/tutorial001_py310.py new file mode 100644 index 00000000..1fbb108b --- /dev/null +++ b/docs_src/tutorial/connect/update/tutorial001_py310.py @@ -0,0 +1,73 @@ +from sqlmodel import Field, Session, 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 create_heroes(): + with Session(engine) as session: + team_preventers = Team(name="Preventers", headquarters="Sharp Tower") + team_z_force = Team(name="Z-Force", headquarters="Sister Margaret’s Bar") + session.add(team_preventers) + session.add(team_z_force) + session.commit() + + hero_deadpond = Hero( + name="Deadpond", secret_name="Dive Wilson", team_id=team_z_force.id + ) + hero_rusty_man = Hero( + name="Rusty-Man", + secret_name="Tommy Sharp", + age=48, + team_id=team_preventers.id, + ) + hero_spider_boy = Hero(name="Spider-Boy", secret_name="Pedro Parqueador") + session.add(hero_deadpond) + session.add(hero_rusty_man) + session.add(hero_spider_boy) + session.commit() + + session.refresh(hero_deadpond) + session.refresh(hero_rusty_man) + session.refresh(hero_spider_boy) + + print("Created hero:", hero_deadpond) + print("Created hero:", hero_rusty_man) + print("Created hero:", hero_spider_boy) + + hero_spider_boy.team_id = team_preventers.id + session.add(hero_spider_boy) + session.commit() + session.refresh(hero_spider_boy) + print("Updated hero:", hero_spider_boy) + + +def main(): + create_db_and_tables() + create_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/create_db_and_table/tutorial001_py310.py b/docs_src/tutorial/create_db_and_table/tutorial001_py310.py new file mode 100644 index 00000000..13ae331b --- /dev/null +++ b/docs_src/tutorial/create_db_and_table/tutorial001_py310.py @@ -0,0 +1,16 @@ +from sqlmodel import Field, 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) + +SQLModel.metadata.create_all(engine) diff --git a/docs_src/tutorial/create_db_and_table/tutorial002_py310.py b/docs_src/tutorial/create_db_and_table/tutorial002_py310.py new file mode 100644 index 00000000..50c7241e --- /dev/null +++ b/docs_src/tutorial/create_db_and_table/tutorial002_py310.py @@ -0,0 +1,22 @@ +from sqlmodel import Field, 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) + + +if __name__ == "__main__": + create_db_and_tables() diff --git a/docs_src/tutorial/create_db_and_table/tutorial003_py310.py b/docs_src/tutorial/create_db_and_table/tutorial003_py310.py new file mode 100644 index 00000000..01fcb14a --- /dev/null +++ b/docs_src/tutorial/create_db_and_table/tutorial003_py310.py @@ -0,0 +1,22 @@ +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)! + + +if __name__ == "__main__": # (13)! + create_db_and_tables() # (14)! diff --git a/docs_src/tutorial/delete/tutorial001_py310.py b/docs_src/tutorial/delete/tutorial001_py310.py new file mode 100644 index 00000000..7c29efa3 --- /dev/null +++ b/docs_src/tutorial/delete/tutorial001_py310.py @@ -0,0 +1,98 @@ +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 = Field(index=True) + secret_name: str + age: int | None = Field(default=None, index=True) + + +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) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def update_heroes(): + with Session(engine) as session: + statement = select(Hero).where(Hero.name == "Spider-Boy") + results = session.exec(statement) + hero_1 = results.one() + print("Hero 1:", hero_1) + + statement = select(Hero).where(Hero.name == "Captain North America") + results = session.exec(statement) + hero_2 = results.one() + print("Hero 2:", hero_2) + + hero_1.age = 16 + hero_1.name = "Spider-Youngster" + session.add(hero_1) + + hero_2.name = "Captain North America Except Canada" + hero_2.age = 110 + session.add(hero_2) + + session.commit() + session.refresh(hero_1) + session.refresh(hero_2) + + print("Updated hero 1:", hero_1) + print("Updated hero 2:", hero_2) + + +def delete_heroes(): + with Session(engine) as session: + statement = select(Hero).where(Hero.name == "Spider-Youngster") + results = session.exec(statement) + hero = results.one() + print("Hero: ", hero) + + session.delete(hero) + session.commit() + + print("Deleted hero:", hero) + + statement = select(Hero).where(Hero.name == "Spider-Youngster") + results = session.exec(statement) + hero = results.first() + + if hero is None: + print("There's no hero named Spider-Youngster") + + +def main(): + create_db_and_tables() + create_heroes() + update_heroes() + delete_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/delete/tutorial002_py310.py b/docs_src/tutorial/delete/tutorial002_py310.py new file mode 100644 index 00000000..afe9a476 --- /dev/null +++ b/docs_src/tutorial/delete/tutorial002_py310.py @@ -0,0 +1,99 @@ +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 = Field(index=True) + secret_name: str + age: int | None = Field(default=None, index=True) + + +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) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def update_heroes(): + with Session(engine) as session: + statement = select(Hero).where(Hero.name == "Spider-Boy") + results = session.exec(statement) + hero_1 = results.one() + print("Hero 1:", hero_1) + + statement = select(Hero).where(Hero.name == "Captain North America") + results = session.exec(statement) + hero_2 = results.one() + print("Hero 2:", hero_2) + + hero_1.age = 16 + hero_1.name = "Spider-Youngster" + session.add(hero_1) + + hero_2.name = "Captain North America Except Canada" + hero_2.age = 110 + session.add(hero_2) + + session.commit() + session.refresh(hero_1) + session.refresh(hero_2) + + print("Updated hero 1:", hero_1) + print("Updated hero 2:", hero_2) + + +def delete_heroes(): + with Session(engine) as session: + statement = select(Hero).where(Hero.name == "Spider-Youngster") # (1)! + results = session.exec(statement) # (2)! + hero = results.one() # (3)! + print("Hero: ", hero) # (4)! + + session.delete(hero) # (5)! + session.commit() # (6)! + + print("Deleted hero:", hero) # (7)! + + statement = select(Hero).where(Hero.name == "Spider-Youngster") # (8)! + results = session.exec(statement) # (9)! + hero = results.first() # (10)! + + if hero is None: # (11)! + print("There's no hero named Spider-Youngster") # (12)! + # (13)! + + +def main(): + create_db_and_tables() + create_heroes() + update_heroes() + delete_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001/test_extra_coverage.py b/docs_src/tutorial/fastapi/app_testing/tutorial001/test_extra_coverage.py new file mode 100644 index 00000000..1d8153ab --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001/test_extra_coverage.py @@ -0,0 +1,38 @@ +from fastapi.testclient import TestClient +from sqlalchemy import Inspector, inspect +from sqlmodel import Session, create_engine + +from . import main as app_mod +from .test_main import client_fixture, session_fixture + +assert client_fixture, "This keeps the client fixture used below" +assert session_fixture, "This keeps the session fixture used by client_fixture" + + +def test_startup(): + app_mod.engine = create_engine("sqlite://") + app_mod.on_startup() + insp: Inspector = inspect(app_mod.engine) + assert insp.has_table(str(app_mod.Hero.__tablename__)) + + +def test_get_session(): + app_mod.engine = create_engine("sqlite://") + for session in app_mod.get_session(): + assert isinstance(session, Session) + assert session.bind == app_mod.engine + + +def test_read_hero_not_found(client: TestClient): + response = client.get("/heroes/9000") + assert response.status_code == 404 + + +def test_update_hero_not_found(client: TestClient): + response = client.patch("/heroes/9000", json={"name": "Very-Rusty-Man"}) + assert response.status_code == 404 + + +def test_delete_hero_not_found(client: TestClient): + response = client.delete("/heroes/9000") + assert response.status_code == 404 diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/__init__.py b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/annotations/en/test_main_001.md b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/annotations/en/test_main_001.md new file mode 100644 index 00000000..936b84b9 --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/annotations/en/test_main_001.md @@ -0,0 +1,17 @@ +1. Import the `app` from the the `main` module. + +2. We create a `TestClient` for the FastAPI `app` and put it in the variable `client`. + +3. Then we use use this `client` to **talk to the API** and send a `POST` HTTP operation, creating a new hero. + +4. Then we get the **JSON data** from the response and put it in the variable `data`. + +5. Next we start testing the results with `assert` statements, we check that the status code of the response is `200`. + +6. We check that the `name` of the hero created is `"Deadpond"`. + +7. We check that the `secret_name` of the hero created is `"Dive Wilson"`. + +8. We check that the `age` of the hero created is `None`, because we didn't send an age. + +9. We check that the hero created has an `id` created by the database, so it's not `None`. diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/annotations/en/test_main_002.md b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/annotations/en/test_main_002.md new file mode 100644 index 00000000..0f8555a8 --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/annotations/en/test_main_002.md @@ -0,0 +1,25 @@ +1. Import the `get_session` dependency from the the `main` module. + +2. Define the new function that will be the new **dependency override**. + +3. This function will return a different **session** than the one that would be returned by the original `get_session` function. + + We haven't seen how this new **session** object is created yet, but the point is that this is a different session than the original one from the app. + + This session is attached to a different **engine**, and that different **engine** uses a different URL, for a database just for testing. + + We haven't defined that new **URL** nor the new **engine** yet, but here we already see the that this object `session` will override the one returned by the original dependency `get_session()`. + +4. Then, the FastAPI `app` object has an attribute `app.dependency_overrides`. + + This attribute is a dictionary, and we can put dependency overrides in it by passing, as the **key**, the **original dependency function**, and as the **value**, the **new overriding dependency function**. + + So, here we are telling the FastAPI app to use `get_session_override` instead of `get_session` in all the places in the code that depend on `get_session`, that is, all the parameters with something like: + + ```Python + session: Session = Depends(get_session) + ``` + +5. After we are done with the dependency override, we can restore the application back to normal, by removing all the values in this dictionary `app.dependency_overrides`. + + This way whenever a *path operation function* needs the dependency FastAPI will use the original one instead of the override. diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/annotations/en/test_main_003.md b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/annotations/en/test_main_003.md new file mode 100644 index 00000000..2b48ebda --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/annotations/en/test_main_003.md @@ -0,0 +1,37 @@ +1. Here's a subtle thing to notice. + + Remember that [Order Matters](../create-db-and-table.md#sqlmodel-metadata-order-matters){.internal-link target=_blank} and we need to make sure all the **SQLModel** models are already defined and **imported** before calling `.create_all()`. + + IN this line, by importing something, *anything*, from `.main`, the code in `.main` will be executed, including the definition of the **table models**, and that will automatically register them in `SQLModel.metadata`. + +2. Here we create a new **engine**, completely different from the one in `main.py`. + + This is the engine we will use for the tests. + + We use the new URL of the database for tests: + + ``` + sqlite:///testing.db + ``` + + And again, we use the connection argument `check_same_thread=False`. + +3. Then we call: + + ```Python + SQLModel.metadata.create_all(engine) + ``` + + ...to make sure we create all the tables in the new testing database. + + The **table models** are registered in `SQLModel.metadata` just because we imported *something* from `.main`, and the code in `.main` was executed, creating the classes for the **table models** and automatically registering them in `SQLModel.metadata`. + + So, by the point we call this method, the **table models** are already registered there. 💯 + +4. Here's where we create the custom **session** object for this test in a `with` block. + + It uses the new custom **engine** we created, so anything that uses this session will be using the testing database. + +5. Now, back to the dependency override, it is just returning the same **session** object from outside, that's it, that's the whole trick. + +6. By this point, the testing **session** `with` block finishes, and the session is closed, the file is closed, etc. diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/annotations/en/test_main_004.md b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/annotations/en/test_main_004.md new file mode 100644 index 00000000..92cbe774 --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/annotations/en/test_main_004.md @@ -0,0 +1,26 @@ +1. Import `StaticPool` from `sqlmodel`, we will use it in a bit. + +2. For the **SQLite URL**, don't write any file name, leave it empty. + + So, instead of: + + ``` + sqlite:///testing.db + ``` + + ...just write: + + ``` + sqlite:// + ``` + + This is enough to tell **SQLModel** (actually SQLAlchemy) that we want to use an **in-memory SQLite database**. + +3. Remember that we told the **low-level** library in charge of communicating with SQLite that we want to be able to **access the database from different threads** with `check_same_thread=False`? + + Now that we use an **in-memory database**, we need to also tell SQLAlchemy that we want to be able to use the **same in-memory database** object from different threads. + + We tell it that with the `poolclass=StaticPool` parameter. + + !!! info + You can read more details in the SQLAlchemy documentation about Using a Memory Database in Multiple Threads diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/annotations/en/test_main_005.md b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/annotations/en/test_main_005.md new file mode 100644 index 00000000..126e1f17 --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/annotations/en/test_main_005.md @@ -0,0 +1,41 @@ +1. Import `pytest`. + +2. Use the `@pytest.fixture()` decorator on top of the function to tell pytest that this is a **fixture** function (equivalent to a FastAPI dependency). + + We also give it a name of `"session"`, this will be important in the testing function. + +3. Create the fixture function. This is equivalent to a FastAPI dependency function. + + In this fixture we create the custom **engine**, with the in-memory database, we create the tables, and we create the **session**. + + Then we `yield` the `session` object. + +4. The thing that we `return` or `yield` is what will be available to the test function, in this case, the `session` object. + + Here we use `yield` so that **pytest** comes back to execute "the rest of the code" in this function once the testing function is done. + + We don't have any more visible "rest of the code" after the `yield`, but we have the end of the `with` block that will close the **session**. + + By using `yield`, pytest will: + + * run the first part + * create the **session** object + * give it to the test function + * run the test function + * once the test function is done, it will continue here, right after the `yield`, and will correctly close the **session** object in the end of the `with` block. + +5. Now, in the test function, to tell **pytest** that this test wants to get the fixture, instead of declaring something like in FastAPI with: + + ```Python + session: Session = Depends(session_fixture) + ``` + + ...the way we tell pytest what is the fixture that we want is by using the **exact same name** of the fixture. + + In this case, we named it `session`, so the parameter has to be exactly named `session` for it to work. + + We also add the type annotation `session: Session` so that we can get autocompletion and inline error checks in our editor. + +6. Now in the dependency override function, we just return the same `session` object that came from outside it. + + The `session` object comes from the parameter passed to the test function, and we just re-use it and return it here in the dependency override. diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/annotations/en/test_main_006.md b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/annotations/en/test_main_006.md new file mode 100644 index 00000000..d44a3b67 --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/annotations/en/test_main_006.md @@ -0,0 +1,23 @@ +1. Create the new fixture named `"client"`. + +2. This **client fixture**, in turn, also requires the **session fixture**. + +3. Now we create the **dependency override** inside the client fixture. + +4. Set the **dependency override** in the `app.dependency_overrides` dictionary. + +5. Create the `TestClient` with the **FastAPI** `app`. + +6. `yield` the `TestClient` instance. + + By using `yield`, after the test function is done, pytest will come back to execute the rest of the code after `yield`. + +7. This is the cleanup code, after `yield`, and after the test function is done. + + Here we clear the dependency overrides (here it's only one) in the FastAPI `app`. + +8. Now the test function requires the **client fixture**. + + And inside the test function, the code is quite **simple**, we just use the `TestClient` to make requests to the API, check the data, and that's it. + + The fixtures take care of all the **setup** and **cleanup** code. diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/main.py b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/main.py new file mode 100644 index 00000000..e8615d91 --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/main.py @@ -0,0 +1,104 @@ +from fastapi import Depends, FastAPI, HTTPException, Query +from sqlmodel import Field, Session, SQLModel, create_engine, select + + +class HeroBase(SQLModel): + name: str = Field(index=True) + secret_name: str + age: int | None = Field(default=None, index=True) + + +class Hero(HeroBase, table=True): + id: int | None = Field(default=None, primary_key=True) + + +class HeroCreate(HeroBase): + pass + + +class HeroRead(HeroBase): + id: int + + +class HeroUpdate(SQLModel): + name: str | None = None + secret_name: str | None = None + age: int | None = None + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +connect_args = {"check_same_thread": False} +engine = create_engine(sqlite_url, echo=True, connect_args=connect_args) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +def get_session(): + with Session(engine) as session: + yield session + + +app = FastAPI() + + +@app.on_event("startup") +def on_startup(): + create_db_and_tables() + + +@app.post("/heroes/", response_model=HeroRead) +def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate): + db_hero = Hero.from_orm(hero) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.get("/heroes/", response_model=list[HeroRead]) +def read_heroes( + *, + session: Session = Depends(get_session), + offset: int = 0, + limit: int = Query(default=100, le=100), +): + heroes = session.exec(select(Hero).offset(offset).limit(limit)).all() + return heroes + + +@app.get("/heroes/{hero_id}", response_model=HeroRead) +def read_hero(*, session: Session = Depends(get_session), hero_id: int): + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + return hero + + +@app.patch("/heroes/{hero_id}", response_model=HeroRead) +def update_hero( + *, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate +): + db_hero = session.get(Hero, hero_id) + if not db_hero: + raise HTTPException(status_code=404, detail="Hero not found") + hero_data = hero.dict(exclude_unset=True) + for key, value in hero_data.items(): + setattr(db_hero, key, value) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.delete("/heroes/{hero_id}") +def delete_hero(*, session: Session = Depends(get_session), hero_id: int): + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + session.delete(hero) + session.commit() + return {"ok": True} diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_extra_coverage.py b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_extra_coverage.py new file mode 100644 index 00000000..1d8153ab --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_extra_coverage.py @@ -0,0 +1,38 @@ +from fastapi.testclient import TestClient +from sqlalchemy import Inspector, inspect +from sqlmodel import Session, create_engine + +from . import main as app_mod +from .test_main import client_fixture, session_fixture + +assert client_fixture, "This keeps the client fixture used below" +assert session_fixture, "This keeps the session fixture used by client_fixture" + + +def test_startup(): + app_mod.engine = create_engine("sqlite://") + app_mod.on_startup() + insp: Inspector = inspect(app_mod.engine) + assert insp.has_table(str(app_mod.Hero.__tablename__)) + + +def test_get_session(): + app_mod.engine = create_engine("sqlite://") + for session in app_mod.get_session(): + assert isinstance(session, Session) + assert session.bind == app_mod.engine + + +def test_read_hero_not_found(client: TestClient): + response = client.get("/heroes/9000") + assert response.status_code == 404 + + +def test_update_hero_not_found(client: TestClient): + response = client.patch("/heroes/9000", json={"name": "Very-Rusty-Man"}) + assert response.status_code == 404 + + +def test_delete_hero_not_found(client: TestClient): + response = client.delete("/heroes/9000") + assert response.status_code == 404 diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_main.py b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_main.py new file mode 100644 index 00000000..435787c7 --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_main.py @@ -0,0 +1,125 @@ +import pytest +from fastapi.testclient import TestClient +from sqlmodel import Session, SQLModel, create_engine +from sqlmodel.pool import StaticPool + +from .main import Hero, app, get_session + + +@pytest.fixture(name="session") +def session_fixture(): + engine = create_engine( + "sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool + ) + SQLModel.metadata.create_all(engine) + with Session(engine) as session: + yield session + + +@pytest.fixture(name="client") +def client_fixture(session: Session): + def get_session_override(): + return session + + app.dependency_overrides[get_session] = get_session_override + client = TestClient(app) + yield client + app.dependency_overrides.clear() + + +def test_create_hero(client: TestClient): + response = client.post( + "/heroes/", json={"name": "Deadpond", "secret_name": "Dive Wilson"} + ) + data = response.json() + + assert response.status_code == 200 + assert data["name"] == "Deadpond" + assert data["secret_name"] == "Dive Wilson" + assert data["age"] is None + assert data["id"] is not None + + +def test_create_hero_incomplete(client: TestClient): + # No secret_name + response = client.post("/heroes/", json={"name": "Deadpond"}) + assert response.status_code == 422 + + +def test_create_hero_invalid(client: TestClient): + # secret_name has an invalid type + response = client.post( + "/heroes/", + json={ + "name": "Deadpond", + "secret_name": {"message": "Do you wanna know my secret identity?"}, + }, + ) + assert response.status_code == 422 + + +def test_read_heroes(session: Session, client: TestClient): + hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson") + hero_2 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48) + session.add(hero_1) + session.add(hero_2) + session.commit() + + response = client.get("/heroes/") + data = response.json() + + assert response.status_code == 200 + + assert len(data) == 2 + assert data[0]["name"] == hero_1.name + assert data[0]["secret_name"] == hero_1.secret_name + assert data[0]["age"] == hero_1.age + assert data[0]["id"] == hero_1.id + assert data[1]["name"] == hero_2.name + assert data[1]["secret_name"] == hero_2.secret_name + assert data[1]["age"] == hero_2.age + assert data[1]["id"] == hero_2.id + + +def test_read_hero(session: Session, client: TestClient): + hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson") + session.add(hero_1) + session.commit() + + response = client.get(f"/heroes/{hero_1.id}") + data = response.json() + + assert response.status_code == 200 + assert data["name"] == hero_1.name + assert data["secret_name"] == hero_1.secret_name + assert data["age"] == hero_1.age + assert data["id"] == hero_1.id + + +def test_update_hero(session: Session, client: TestClient): + hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson") + session.add(hero_1) + session.commit() + + response = client.patch(f"/heroes/{hero_1.id}", json={"name": "Deadpuddle"}) + data = response.json() + + assert response.status_code == 200 + assert data["name"] == "Deadpuddle" + assert data["secret_name"] == "Dive Wilson" + assert data["age"] is None + assert data["id"] == hero_1.id + + +def test_delete_hero(session: Session, client: TestClient): + hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson") + session.add(hero_1) + session.commit() + + response = client.delete(f"/heroes/{hero_1.id}") + + hero_in_db = session.get(Hero, hero_1.id) + + assert response.status_code == 200 + + assert hero_in_db is None diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_main_001.py b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_main_001.py new file mode 100644 index 00000000..3ae40773 --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_main_001.py @@ -0,0 +1,32 @@ +from fastapi.testclient import TestClient +from sqlmodel import Session, SQLModel, create_engine + +from .main import app, get_session # (1)! + + +def test_create_hero(): + engine = create_engine( + "sqlite:///testing.db", connect_args={"check_same_thread": False} + ) + SQLModel.metadata.create_all(engine) + + with Session(engine) as session: + + def get_session_override(): + return session + + app.dependency_overrides[get_session] = get_session_override + + client = TestClient(app) # (2)! + + response = client.post( # (3)! + "/heroes/", json={"name": "Deadpond", "secret_name": "Dive Wilson"} + ) + app.dependency_overrides.clear() + data = response.json() # (4)! + + assert response.status_code == 200 # (5)! + assert data["name"] == "Deadpond" # (6)! + assert data["secret_name"] == "Dive Wilson" # (7)! + assert data["age"] is None # (8)! + assert data["id"] is not None # (9)! diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_main_002.py b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_main_002.py new file mode 100644 index 00000000..727580b6 --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_main_002.py @@ -0,0 +1,32 @@ +from fastapi.testclient import TestClient +from sqlmodel import Session, SQLModel, create_engine + +from .main import app, get_session # (1)! + + +def test_create_hero(): + engine = create_engine( + "sqlite:///testing.db", connect_args={"check_same_thread": False} + ) + SQLModel.metadata.create_all(engine) + + with Session(engine) as session: + + def get_session_override(): # (2)! + return session # (3)! + + app.dependency_overrides[get_session] = get_session_override # (4)! + + client = TestClient(app) + + response = client.post( + "/heroes/", json={"name": "Deadpond", "secret_name": "Dive Wilson"} + ) + app.dependency_overrides.clear() # (5)! + data = response.json() + + assert response.status_code == 200 + assert data["name"] == "Deadpond" + assert data["secret_name"] == "Dive Wilson" + assert data["age"] is None + assert data["id"] is not None diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_main_003.py b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_main_003.py new file mode 100644 index 00000000..465c5251 --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_main_003.py @@ -0,0 +1,33 @@ +from fastapi.testclient import TestClient +from sqlmodel import Session, SQLModel, create_engine + +from .main import app, get_session # (1)! + + +def test_create_hero(): + engine = create_engine( # (2)! + "sqlite:///testing.db", connect_args={"check_same_thread": False} + ) + SQLModel.metadata.create_all(engine) # (3)! + + with Session(engine) as session: # (4)! + + def get_session_override(): + return session # (5)! + + app.dependency_overrides[get_session] = get_session_override # (4)! + + client = TestClient(app) + + response = client.post( + "/heroes/", json={"name": "Deadpond", "secret_name": "Dive Wilson"} + ) + app.dependency_overrides.clear() + data = response.json() + + assert response.status_code == 200 + assert data["name"] == "Deadpond" + assert data["secret_name"] == "Dive Wilson" + assert data["age"] is None + assert data["id"] is not None + # (6)! diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_main_004.py b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_main_004.py new file mode 100644 index 00000000..b770a9aa --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_main_004.py @@ -0,0 +1,35 @@ +from fastapi.testclient import TestClient +from sqlmodel import Session, SQLModel, create_engine +from sqlmodel.pool import StaticPool # (1)! + +from .main import app, get_session + + +def test_create_hero(): + engine = create_engine( + "sqlite://", # (2)! + connect_args={"check_same_thread": False}, + poolclass=StaticPool, # (3)! + ) + SQLModel.metadata.create_all(engine) + + with Session(engine) as session: + + def get_session_override(): + return session + + app.dependency_overrides[get_session] = get_session_override + + client = TestClient(app) + + response = client.post( + "/heroes/", json={"name": "Deadpond", "secret_name": "Dive Wilson"} + ) + app.dependency_overrides.clear() + data = response.json() + + assert response.status_code == 200 + assert data["name"] == "Deadpond" + assert data["secret_name"] == "Dive Wilson" + assert data["age"] is None + assert data["id"] is not None diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_main_005.py b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_main_005.py new file mode 100644 index 00000000..f653eef7 --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_main_005.py @@ -0,0 +1,37 @@ +import pytest # (1)! +from fastapi.testclient import TestClient +from sqlmodel import Session, SQLModel, create_engine +from sqlmodel.pool import StaticPool + +from .main import app, get_session + + +@pytest.fixture(name="session") # (2)! +def session_fixture(): # (3)! + engine = create_engine( + "sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool + ) + SQLModel.metadata.create_all(engine) + with Session(engine) as session: + yield session # (4)! + + +def test_create_hero(session: Session): # (5)! + def get_session_override(): + return session # (6)! + + app.dependency_overrides[get_session] = get_session_override + + client = TestClient(app) + + response = client.post( + "/heroes/", json={"name": "Deadpond", "secret_name": "Dive Wilson"} + ) + app.dependency_overrides.clear() + data = response.json() + + assert response.status_code == 200 + assert data["name"] == "Deadpond" + assert data["secret_name"] == "Dive Wilson" + assert data["age"] is None + assert data["id"] is not None diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_main_006.py b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_main_006.py new file mode 100644 index 00000000..8dbfd45c --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_main_006.py @@ -0,0 +1,41 @@ +import pytest +from fastapi.testclient import TestClient +from sqlmodel import Session, SQLModel, create_engine +from sqlmodel.pool import StaticPool + +from .main import app, get_session + + +@pytest.fixture(name="session") +def session_fixture(): + engine = create_engine( + "sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool + ) + SQLModel.metadata.create_all(engine) + with Session(engine) as session: + yield session + + +@pytest.fixture(name="client") # (1)! +def client_fixture(session: Session): # (2)! + def get_session_override(): # (3)! + return session + + app.dependency_overrides[get_session] = get_session_override # (4)! + + client = TestClient(app) # (5)! + yield client # (6)! + app.dependency_overrides.clear() # (7)! + + +def test_create_hero(client: TestClient): # (8)! + response = client.post( + "/heroes/", json={"name": "Deadpond", "secret_name": "Dive Wilson"} + ) + data = response.json() + + assert response.status_code == 200 + assert data["name"] == "Deadpond" + assert data["secret_name"] == "Dive Wilson" + assert data["age"] is None + assert data["id"] is not None diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/__init__.py b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/annotations/en/test_main_001.md b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/annotations/en/test_main_001.md new file mode 100644 index 00000000..936b84b9 --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/annotations/en/test_main_001.md @@ -0,0 +1,17 @@ +1. Import the `app` from the the `main` module. + +2. We create a `TestClient` for the FastAPI `app` and put it in the variable `client`. + +3. Then we use use this `client` to **talk to the API** and send a `POST` HTTP operation, creating a new hero. + +4. Then we get the **JSON data** from the response and put it in the variable `data`. + +5. Next we start testing the results with `assert` statements, we check that the status code of the response is `200`. + +6. We check that the `name` of the hero created is `"Deadpond"`. + +7. We check that the `secret_name` of the hero created is `"Dive Wilson"`. + +8. We check that the `age` of the hero created is `None`, because we didn't send an age. + +9. We check that the hero created has an `id` created by the database, so it's not `None`. diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/annotations/en/test_main_002.md b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/annotations/en/test_main_002.md new file mode 100644 index 00000000..0f8555a8 --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/annotations/en/test_main_002.md @@ -0,0 +1,25 @@ +1. Import the `get_session` dependency from the the `main` module. + +2. Define the new function that will be the new **dependency override**. + +3. This function will return a different **session** than the one that would be returned by the original `get_session` function. + + We haven't seen how this new **session** object is created yet, but the point is that this is a different session than the original one from the app. + + This session is attached to a different **engine**, and that different **engine** uses a different URL, for a database just for testing. + + We haven't defined that new **URL** nor the new **engine** yet, but here we already see the that this object `session` will override the one returned by the original dependency `get_session()`. + +4. Then, the FastAPI `app` object has an attribute `app.dependency_overrides`. + + This attribute is a dictionary, and we can put dependency overrides in it by passing, as the **key**, the **original dependency function**, and as the **value**, the **new overriding dependency function**. + + So, here we are telling the FastAPI app to use `get_session_override` instead of `get_session` in all the places in the code that depend on `get_session`, that is, all the parameters with something like: + + ```Python + session: Session = Depends(get_session) + ``` + +5. After we are done with the dependency override, we can restore the application back to normal, by removing all the values in this dictionary `app.dependency_overrides`. + + This way whenever a *path operation function* needs the dependency FastAPI will use the original one instead of the override. diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/annotations/en/test_main_003.md b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/annotations/en/test_main_003.md new file mode 100644 index 00000000..2b48ebda --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/annotations/en/test_main_003.md @@ -0,0 +1,37 @@ +1. Here's a subtle thing to notice. + + Remember that [Order Matters](../create-db-and-table.md#sqlmodel-metadata-order-matters){.internal-link target=_blank} and we need to make sure all the **SQLModel** models are already defined and **imported** before calling `.create_all()`. + + IN this line, by importing something, *anything*, from `.main`, the code in `.main` will be executed, including the definition of the **table models**, and that will automatically register them in `SQLModel.metadata`. + +2. Here we create a new **engine**, completely different from the one in `main.py`. + + This is the engine we will use for the tests. + + We use the new URL of the database for tests: + + ``` + sqlite:///testing.db + ``` + + And again, we use the connection argument `check_same_thread=False`. + +3. Then we call: + + ```Python + SQLModel.metadata.create_all(engine) + ``` + + ...to make sure we create all the tables in the new testing database. + + The **table models** are registered in `SQLModel.metadata` just because we imported *something* from `.main`, and the code in `.main` was executed, creating the classes for the **table models** and automatically registering them in `SQLModel.metadata`. + + So, by the point we call this method, the **table models** are already registered there. 💯 + +4. Here's where we create the custom **session** object for this test in a `with` block. + + It uses the new custom **engine** we created, so anything that uses this session will be using the testing database. + +5. Now, back to the dependency override, it is just returning the same **session** object from outside, that's it, that's the whole trick. + +6. By this point, the testing **session** `with` block finishes, and the session is closed, the file is closed, etc. diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/annotations/en/test_main_004.md b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/annotations/en/test_main_004.md new file mode 100644 index 00000000..92cbe774 --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/annotations/en/test_main_004.md @@ -0,0 +1,26 @@ +1. Import `StaticPool` from `sqlmodel`, we will use it in a bit. + +2. For the **SQLite URL**, don't write any file name, leave it empty. + + So, instead of: + + ``` + sqlite:///testing.db + ``` + + ...just write: + + ``` + sqlite:// + ``` + + This is enough to tell **SQLModel** (actually SQLAlchemy) that we want to use an **in-memory SQLite database**. + +3. Remember that we told the **low-level** library in charge of communicating with SQLite that we want to be able to **access the database from different threads** with `check_same_thread=False`? + + Now that we use an **in-memory database**, we need to also tell SQLAlchemy that we want to be able to use the **same in-memory database** object from different threads. + + We tell it that with the `poolclass=StaticPool` parameter. + + !!! info + You can read more details in the SQLAlchemy documentation about Using a Memory Database in Multiple Threads diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/annotations/en/test_main_005.md b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/annotations/en/test_main_005.md new file mode 100644 index 00000000..126e1f17 --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/annotations/en/test_main_005.md @@ -0,0 +1,41 @@ +1. Import `pytest`. + +2. Use the `@pytest.fixture()` decorator on top of the function to tell pytest that this is a **fixture** function (equivalent to a FastAPI dependency). + + We also give it a name of `"session"`, this will be important in the testing function. + +3. Create the fixture function. This is equivalent to a FastAPI dependency function. + + In this fixture we create the custom **engine**, with the in-memory database, we create the tables, and we create the **session**. + + Then we `yield` the `session` object. + +4. The thing that we `return` or `yield` is what will be available to the test function, in this case, the `session` object. + + Here we use `yield` so that **pytest** comes back to execute "the rest of the code" in this function once the testing function is done. + + We don't have any more visible "rest of the code" after the `yield`, but we have the end of the `with` block that will close the **session**. + + By using `yield`, pytest will: + + * run the first part + * create the **session** object + * give it to the test function + * run the test function + * once the test function is done, it will continue here, right after the `yield`, and will correctly close the **session** object in the end of the `with` block. + +5. Now, in the test function, to tell **pytest** that this test wants to get the fixture, instead of declaring something like in FastAPI with: + + ```Python + session: Session = Depends(session_fixture) + ``` + + ...the way we tell pytest what is the fixture that we want is by using the **exact same name** of the fixture. + + In this case, we named it `session`, so the parameter has to be exactly named `session` for it to work. + + We also add the type annotation `session: Session` so that we can get autocompletion and inline error checks in our editor. + +6. Now in the dependency override function, we just return the same `session` object that came from outside it. + + The `session` object comes from the parameter passed to the test function, and we just re-use it and return it here in the dependency override. diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/annotations/en/test_main_006.md b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/annotations/en/test_main_006.md new file mode 100644 index 00000000..d44a3b67 --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/annotations/en/test_main_006.md @@ -0,0 +1,23 @@ +1. Create the new fixture named `"client"`. + +2. This **client fixture**, in turn, also requires the **session fixture**. + +3. Now we create the **dependency override** inside the client fixture. + +4. Set the **dependency override** in the `app.dependency_overrides` dictionary. + +5. Create the `TestClient` with the **FastAPI** `app`. + +6. `yield` the `TestClient` instance. + + By using `yield`, after the test function is done, pytest will come back to execute the rest of the code after `yield`. + +7. This is the cleanup code, after `yield`, and after the test function is done. + + Here we clear the dependency overrides (here it's only one) in the FastAPI `app`. + +8. Now the test function requires the **client fixture**. + + And inside the test function, the code is quite **simple**, we just use the `TestClient` to make requests to the API, check the data, and that's it. + + The fixtures take care of all the **setup** and **cleanup** code. diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/main.py b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/main.py new file mode 100644 index 00000000..9816e70e --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/main.py @@ -0,0 +1,106 @@ +from typing import Optional + +from fastapi import Depends, FastAPI, HTTPException, Query +from sqlmodel import Field, Session, SQLModel, create_engine, select + + +class HeroBase(SQLModel): + name: str = Field(index=True) + secret_name: str + age: Optional[int] = Field(default=None, index=True) + + +class Hero(HeroBase, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + + +class HeroCreate(HeroBase): + pass + + +class HeroRead(HeroBase): + id: int + + +class HeroUpdate(SQLModel): + name: Optional[str] = None + secret_name: Optional[str] = None + age: Optional[int] = None + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +connect_args = {"check_same_thread": False} +engine = create_engine(sqlite_url, echo=True, connect_args=connect_args) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +def get_session(): + with Session(engine) as session: + yield session + + +app = FastAPI() + + +@app.on_event("startup") +def on_startup(): + create_db_and_tables() + + +@app.post("/heroes/", response_model=HeroRead) +def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate): + db_hero = Hero.from_orm(hero) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.get("/heroes/", response_model=list[HeroRead]) +def read_heroes( + *, + session: Session = Depends(get_session), + offset: int = 0, + limit: int = Query(default=100, le=100), +): + heroes = session.exec(select(Hero).offset(offset).limit(limit)).all() + return heroes + + +@app.get("/heroes/{hero_id}", response_model=HeroRead) +def read_hero(*, session: Session = Depends(get_session), hero_id: int): + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + return hero + + +@app.patch("/heroes/{hero_id}", response_model=HeroRead) +def update_hero( + *, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate +): + db_hero = session.get(Hero, hero_id) + if not db_hero: + raise HTTPException(status_code=404, detail="Hero not found") + hero_data = hero.dict(exclude_unset=True) + for key, value in hero_data.items(): + setattr(db_hero, key, value) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.delete("/heroes/{hero_id}") +def delete_hero(*, session: Session = Depends(get_session), hero_id: int): + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + session.delete(hero) + session.commit() + return {"ok": True} diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_extra_coverage.py b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_extra_coverage.py new file mode 100644 index 00000000..1d8153ab --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_extra_coverage.py @@ -0,0 +1,38 @@ +from fastapi.testclient import TestClient +from sqlalchemy import Inspector, inspect +from sqlmodel import Session, create_engine + +from . import main as app_mod +from .test_main import client_fixture, session_fixture + +assert client_fixture, "This keeps the client fixture used below" +assert session_fixture, "This keeps the session fixture used by client_fixture" + + +def test_startup(): + app_mod.engine = create_engine("sqlite://") + app_mod.on_startup() + insp: Inspector = inspect(app_mod.engine) + assert insp.has_table(str(app_mod.Hero.__tablename__)) + + +def test_get_session(): + app_mod.engine = create_engine("sqlite://") + for session in app_mod.get_session(): + assert isinstance(session, Session) + assert session.bind == app_mod.engine + + +def test_read_hero_not_found(client: TestClient): + response = client.get("/heroes/9000") + assert response.status_code == 404 + + +def test_update_hero_not_found(client: TestClient): + response = client.patch("/heroes/9000", json={"name": "Very-Rusty-Man"}) + assert response.status_code == 404 + + +def test_delete_hero_not_found(client: TestClient): + response = client.delete("/heroes/9000") + assert response.status_code == 404 diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_main.py b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_main.py new file mode 100644 index 00000000..435787c7 --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_main.py @@ -0,0 +1,125 @@ +import pytest +from fastapi.testclient import TestClient +from sqlmodel import Session, SQLModel, create_engine +from sqlmodel.pool import StaticPool + +from .main import Hero, app, get_session + + +@pytest.fixture(name="session") +def session_fixture(): + engine = create_engine( + "sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool + ) + SQLModel.metadata.create_all(engine) + with Session(engine) as session: + yield session + + +@pytest.fixture(name="client") +def client_fixture(session: Session): + def get_session_override(): + return session + + app.dependency_overrides[get_session] = get_session_override + client = TestClient(app) + yield client + app.dependency_overrides.clear() + + +def test_create_hero(client: TestClient): + response = client.post( + "/heroes/", json={"name": "Deadpond", "secret_name": "Dive Wilson"} + ) + data = response.json() + + assert response.status_code == 200 + assert data["name"] == "Deadpond" + assert data["secret_name"] == "Dive Wilson" + assert data["age"] is None + assert data["id"] is not None + + +def test_create_hero_incomplete(client: TestClient): + # No secret_name + response = client.post("/heroes/", json={"name": "Deadpond"}) + assert response.status_code == 422 + + +def test_create_hero_invalid(client: TestClient): + # secret_name has an invalid type + response = client.post( + "/heroes/", + json={ + "name": "Deadpond", + "secret_name": {"message": "Do you wanna know my secret identity?"}, + }, + ) + assert response.status_code == 422 + + +def test_read_heroes(session: Session, client: TestClient): + hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson") + hero_2 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48) + session.add(hero_1) + session.add(hero_2) + session.commit() + + response = client.get("/heroes/") + data = response.json() + + assert response.status_code == 200 + + assert len(data) == 2 + assert data[0]["name"] == hero_1.name + assert data[0]["secret_name"] == hero_1.secret_name + assert data[0]["age"] == hero_1.age + assert data[0]["id"] == hero_1.id + assert data[1]["name"] == hero_2.name + assert data[1]["secret_name"] == hero_2.secret_name + assert data[1]["age"] == hero_2.age + assert data[1]["id"] == hero_2.id + + +def test_read_hero(session: Session, client: TestClient): + hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson") + session.add(hero_1) + session.commit() + + response = client.get(f"/heroes/{hero_1.id}") + data = response.json() + + assert response.status_code == 200 + assert data["name"] == hero_1.name + assert data["secret_name"] == hero_1.secret_name + assert data["age"] == hero_1.age + assert data["id"] == hero_1.id + + +def test_update_hero(session: Session, client: TestClient): + hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson") + session.add(hero_1) + session.commit() + + response = client.patch(f"/heroes/{hero_1.id}", json={"name": "Deadpuddle"}) + data = response.json() + + assert response.status_code == 200 + assert data["name"] == "Deadpuddle" + assert data["secret_name"] == "Dive Wilson" + assert data["age"] is None + assert data["id"] == hero_1.id + + +def test_delete_hero(session: Session, client: TestClient): + hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson") + session.add(hero_1) + session.commit() + + response = client.delete(f"/heroes/{hero_1.id}") + + hero_in_db = session.get(Hero, hero_1.id) + + assert response.status_code == 200 + + assert hero_in_db is None diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_main_001.py b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_main_001.py new file mode 100644 index 00000000..3ae40773 --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_main_001.py @@ -0,0 +1,32 @@ +from fastapi.testclient import TestClient +from sqlmodel import Session, SQLModel, create_engine + +from .main import app, get_session # (1)! + + +def test_create_hero(): + engine = create_engine( + "sqlite:///testing.db", connect_args={"check_same_thread": False} + ) + SQLModel.metadata.create_all(engine) + + with Session(engine) as session: + + def get_session_override(): + return session + + app.dependency_overrides[get_session] = get_session_override + + client = TestClient(app) # (2)! + + response = client.post( # (3)! + "/heroes/", json={"name": "Deadpond", "secret_name": "Dive Wilson"} + ) + app.dependency_overrides.clear() + data = response.json() # (4)! + + assert response.status_code == 200 # (5)! + assert data["name"] == "Deadpond" # (6)! + assert data["secret_name"] == "Dive Wilson" # (7)! + assert data["age"] is None # (8)! + assert data["id"] is not None # (9)! diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_main_002.py b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_main_002.py new file mode 100644 index 00000000..727580b6 --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_main_002.py @@ -0,0 +1,32 @@ +from fastapi.testclient import TestClient +from sqlmodel import Session, SQLModel, create_engine + +from .main import app, get_session # (1)! + + +def test_create_hero(): + engine = create_engine( + "sqlite:///testing.db", connect_args={"check_same_thread": False} + ) + SQLModel.metadata.create_all(engine) + + with Session(engine) as session: + + def get_session_override(): # (2)! + return session # (3)! + + app.dependency_overrides[get_session] = get_session_override # (4)! + + client = TestClient(app) + + response = client.post( + "/heroes/", json={"name": "Deadpond", "secret_name": "Dive Wilson"} + ) + app.dependency_overrides.clear() # (5)! + data = response.json() + + assert response.status_code == 200 + assert data["name"] == "Deadpond" + assert data["secret_name"] == "Dive Wilson" + assert data["age"] is None + assert data["id"] is not None diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_main_003.py b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_main_003.py new file mode 100644 index 00000000..465c5251 --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_main_003.py @@ -0,0 +1,33 @@ +from fastapi.testclient import TestClient +from sqlmodel import Session, SQLModel, create_engine + +from .main import app, get_session # (1)! + + +def test_create_hero(): + engine = create_engine( # (2)! + "sqlite:///testing.db", connect_args={"check_same_thread": False} + ) + SQLModel.metadata.create_all(engine) # (3)! + + with Session(engine) as session: # (4)! + + def get_session_override(): + return session # (5)! + + app.dependency_overrides[get_session] = get_session_override # (4)! + + client = TestClient(app) + + response = client.post( + "/heroes/", json={"name": "Deadpond", "secret_name": "Dive Wilson"} + ) + app.dependency_overrides.clear() + data = response.json() + + assert response.status_code == 200 + assert data["name"] == "Deadpond" + assert data["secret_name"] == "Dive Wilson" + assert data["age"] is None + assert data["id"] is not None + # (6)! diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_main_004.py b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_main_004.py new file mode 100644 index 00000000..b770a9aa --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_main_004.py @@ -0,0 +1,35 @@ +from fastapi.testclient import TestClient +from sqlmodel import Session, SQLModel, create_engine +from sqlmodel.pool import StaticPool # (1)! + +from .main import app, get_session + + +def test_create_hero(): + engine = create_engine( + "sqlite://", # (2)! + connect_args={"check_same_thread": False}, + poolclass=StaticPool, # (3)! + ) + SQLModel.metadata.create_all(engine) + + with Session(engine) as session: + + def get_session_override(): + return session + + app.dependency_overrides[get_session] = get_session_override + + client = TestClient(app) + + response = client.post( + "/heroes/", json={"name": "Deadpond", "secret_name": "Dive Wilson"} + ) + app.dependency_overrides.clear() + data = response.json() + + assert response.status_code == 200 + assert data["name"] == "Deadpond" + assert data["secret_name"] == "Dive Wilson" + assert data["age"] is None + assert data["id"] is not None diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_main_005.py b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_main_005.py new file mode 100644 index 00000000..f653eef7 --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_main_005.py @@ -0,0 +1,37 @@ +import pytest # (1)! +from fastapi.testclient import TestClient +from sqlmodel import Session, SQLModel, create_engine +from sqlmodel.pool import StaticPool + +from .main import app, get_session + + +@pytest.fixture(name="session") # (2)! +def session_fixture(): # (3)! + engine = create_engine( + "sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool + ) + SQLModel.metadata.create_all(engine) + with Session(engine) as session: + yield session # (4)! + + +def test_create_hero(session: Session): # (5)! + def get_session_override(): + return session # (6)! + + app.dependency_overrides[get_session] = get_session_override + + client = TestClient(app) + + response = client.post( + "/heroes/", json={"name": "Deadpond", "secret_name": "Dive Wilson"} + ) + app.dependency_overrides.clear() + data = response.json() + + assert response.status_code == 200 + assert data["name"] == "Deadpond" + assert data["secret_name"] == "Dive Wilson" + assert data["age"] is None + assert data["id"] is not None diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_main_006.py b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_main_006.py new file mode 100644 index 00000000..8dbfd45c --- /dev/null +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_main_006.py @@ -0,0 +1,41 @@ +import pytest +from fastapi.testclient import TestClient +from sqlmodel import Session, SQLModel, create_engine +from sqlmodel.pool import StaticPool + +from .main import app, get_session + + +@pytest.fixture(name="session") +def session_fixture(): + engine = create_engine( + "sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool + ) + SQLModel.metadata.create_all(engine) + with Session(engine) as session: + yield session + + +@pytest.fixture(name="client") # (1)! +def client_fixture(session: Session): # (2)! + def get_session_override(): # (3)! + return session + + app.dependency_overrides[get_session] = get_session_override # (4)! + + client = TestClient(app) # (5)! + yield client # (6)! + app.dependency_overrides.clear() # (7)! + + +def test_create_hero(client: TestClient): # (8)! + response = client.post( + "/heroes/", json={"name": "Deadpond", "secret_name": "Dive Wilson"} + ) + data = response.json() + + assert response.status_code == 200 + assert data["name"] == "Deadpond" + assert data["secret_name"] == "Dive Wilson" + assert data["age"] is None + assert data["id"] is not None diff --git a/docs_src/tutorial/fastapi/delete/tutorial001_py310.py b/docs_src/tutorial/fastapi/delete/tutorial001_py310.py new file mode 100644 index 00000000..5b2da0a0 --- /dev/null +++ b/docs_src/tutorial/fastapi/delete/tutorial001_py310.py @@ -0,0 +1,97 @@ +from fastapi import FastAPI, HTTPException, Query +from sqlmodel import Field, Session, SQLModel, create_engine, select + + +class HeroBase(SQLModel): + name: str = Field(index=True) + secret_name: str + age: int | None = Field(default=None, index=True) + + +class Hero(HeroBase, table=True): + id: int | None = Field(default=None, primary_key=True) + + +class HeroCreate(HeroBase): + pass + + +class HeroRead(HeroBase): + id: int + + +class HeroUpdate(SQLModel): + name: str | None = None + secret_name: str | None = None + age: int | None = None + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +connect_args = {"check_same_thread": False} +engine = create_engine(sqlite_url, echo=True, connect_args=connect_args) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +app = FastAPI() + + +@app.on_event("startup") +def on_startup(): + create_db_and_tables() + + +@app.post("/heroes/", response_model=HeroRead) +def create_hero(hero: HeroCreate): + with Session(engine) as session: + db_hero = Hero.from_orm(hero) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.get("/heroes/", response_model=list[HeroRead]) +def read_heroes(offset: int = 0, limit: int = Query(default=100, le=100)): + with Session(engine) as session: + heroes = session.exec(select(Hero).offset(offset).limit(limit)).all() + return heroes + + +@app.get("/heroes/{hero_id}", response_model=HeroRead) +def read_hero(hero_id: int): + with Session(engine) as session: + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + return hero + + +@app.patch("/heroes/{hero_id}", response_model=HeroRead) +def update_hero(hero_id: int, hero: HeroUpdate): + with Session(engine) as session: + db_hero = session.get(Hero, hero_id) + if not db_hero: + raise HTTPException(status_code=404, detail="Hero not found") + hero_data = hero.dict(exclude_unset=True) + for key, value in hero_data.items(): + setattr(db_hero, key, value) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.delete("/heroes/{hero_id}") +def delete_hero(hero_id: int): + with Session(engine) as session: + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + session.delete(hero) + session.commit() + return {"ok": True} diff --git a/docs_src/tutorial/fastapi/delete/tutorial001_py39.py b/docs_src/tutorial/fastapi/delete/tutorial001_py39.py new file mode 100644 index 00000000..5f498cf1 --- /dev/null +++ b/docs_src/tutorial/fastapi/delete/tutorial001_py39.py @@ -0,0 +1,99 @@ +from typing import Optional + +from fastapi import FastAPI, HTTPException, Query +from sqlmodel import Field, Session, SQLModel, create_engine, select + + +class HeroBase(SQLModel): + name: str = Field(index=True) + secret_name: str + age: Optional[int] = Field(default=None, index=True) + + +class Hero(HeroBase, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + + +class HeroCreate(HeroBase): + pass + + +class HeroRead(HeroBase): + id: int + + +class HeroUpdate(SQLModel): + name: Optional[str] = None + secret_name: Optional[str] = None + age: Optional[int] = None + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +connect_args = {"check_same_thread": False} +engine = create_engine(sqlite_url, echo=True, connect_args=connect_args) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +app = FastAPI() + + +@app.on_event("startup") +def on_startup(): + create_db_and_tables() + + +@app.post("/heroes/", response_model=HeroRead) +def create_hero(hero: HeroCreate): + with Session(engine) as session: + db_hero = Hero.from_orm(hero) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.get("/heroes/", response_model=list[HeroRead]) +def read_heroes(offset: int = 0, limit: int = Query(default=100, le=100)): + with Session(engine) as session: + heroes = session.exec(select(Hero).offset(offset).limit(limit)).all() + return heroes + + +@app.get("/heroes/{hero_id}", response_model=HeroRead) +def read_hero(hero_id: int): + with Session(engine) as session: + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + return hero + + +@app.patch("/heroes/{hero_id}", response_model=HeroRead) +def update_hero(hero_id: int, hero: HeroUpdate): + with Session(engine) as session: + db_hero = session.get(Hero, hero_id) + if not db_hero: + raise HTTPException(status_code=404, detail="Hero not found") + hero_data = hero.dict(exclude_unset=True) + for key, value in hero_data.items(): + setattr(db_hero, key, value) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.delete("/heroes/{hero_id}") +def delete_hero(hero_id: int): + with Session(engine) as session: + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + session.delete(hero) + session.commit() + return {"ok": True} diff --git a/docs_src/tutorial/fastapi/limit_and_offset/tutorial001_py310.py b/docs_src/tutorial/fastapi/limit_and_offset/tutorial001_py310.py new file mode 100644 index 00000000..874a6e84 --- /dev/null +++ b/docs_src/tutorial/fastapi/limit_and_offset/tutorial001_py310.py @@ -0,0 +1,65 @@ +from fastapi import FastAPI, HTTPException, Query +from sqlmodel import Field, Session, SQLModel, create_engine, select + + +class HeroBase(SQLModel): + name: str = Field(index=True) + secret_name: str + age: int | None = Field(default=None, index=True) + + +class Hero(HeroBase, table=True): + id: int | None = Field(default=None, primary_key=True) + + +class HeroCreate(HeroBase): + pass + + +class HeroRead(HeroBase): + id: int + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +connect_args = {"check_same_thread": False} +engine = create_engine(sqlite_url, echo=True, connect_args=connect_args) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +app = FastAPI() + + +@app.on_event("startup") +def on_startup(): + create_db_and_tables() + + +@app.post("/heroes/", response_model=HeroRead) +def create_hero(hero: HeroCreate): + with Session(engine) as session: + db_hero = Hero.from_orm(hero) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.get("/heroes/", response_model=list[HeroRead]) +def read_heroes(offset: int = 0, limit: int = Query(default=100, le=100)): + with Session(engine) as session: + heroes = session.exec(select(Hero).offset(offset).limit(limit)).all() + return heroes + + +@app.get("/heroes/{hero_id}", response_model=HeroRead) +def read_hero(hero_id: int): + with Session(engine) as session: + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + return hero diff --git a/docs_src/tutorial/fastapi/limit_and_offset/tutorial001_py39.py b/docs_src/tutorial/fastapi/limit_and_offset/tutorial001_py39.py new file mode 100644 index 00000000..b63fa753 --- /dev/null +++ b/docs_src/tutorial/fastapi/limit_and_offset/tutorial001_py39.py @@ -0,0 +1,67 @@ +from typing import Optional + +from fastapi import FastAPI, HTTPException, Query +from sqlmodel import Field, Session, SQLModel, create_engine, select + + +class HeroBase(SQLModel): + name: str = Field(index=True) + secret_name: str + age: Optional[int] = Field(default=None, index=True) + + +class Hero(HeroBase, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + + +class HeroCreate(HeroBase): + pass + + +class HeroRead(HeroBase): + id: int + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +connect_args = {"check_same_thread": False} +engine = create_engine(sqlite_url, echo=True, connect_args=connect_args) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +app = FastAPI() + + +@app.on_event("startup") +def on_startup(): + create_db_and_tables() + + +@app.post("/heroes/", response_model=HeroRead) +def create_hero(hero: HeroCreate): + with Session(engine) as session: + db_hero = Hero.from_orm(hero) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.get("/heroes/", response_model=list[HeroRead]) +def read_heroes(offset: int = 0, limit: int = Query(default=100, le=100)): + with Session(engine) as session: + heroes = session.exec(select(Hero).offset(offset).limit(limit)).all() + return heroes + + +@app.get("/heroes/{hero_id}", response_model=HeroRead) +def read_hero(hero_id: int): + with Session(engine) as session: + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + return hero diff --git a/docs_src/tutorial/fastapi/multiple_models/tutorial001_py310.py b/docs_src/tutorial/fastapi/multiple_models/tutorial001_py310.py new file mode 100644 index 00000000..13129f38 --- /dev/null +++ b/docs_src/tutorial/fastapi/multiple_models/tutorial001_py310.py @@ -0,0 +1,58 @@ +from fastapi import FastAPI +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 = Field(index=True) + secret_name: str + age: int | None = Field(default=None, index=True) + + +class HeroCreate(SQLModel): + name: str + secret_name: str + age: int | None = None + + +class HeroRead(SQLModel): + id: int + name: str + secret_name: str + age: int | None = None + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +connect_args = {"check_same_thread": False} +engine = create_engine(sqlite_url, echo=True, connect_args=connect_args) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +app = FastAPI() + + +@app.on_event("startup") +def on_startup(): + create_db_and_tables() + + +@app.post("/heroes/", response_model=HeroRead) +def create_hero(hero: HeroCreate): + with Session(engine) as session: + db_hero = Hero.from_orm(hero) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.get("/heroes/", response_model=list[HeroRead]) +def read_heroes(): + with Session(engine) as session: + heroes = session.exec(select(Hero)).all() + return heroes diff --git a/docs_src/tutorial/fastapi/multiple_models/tutorial001_py39.py b/docs_src/tutorial/fastapi/multiple_models/tutorial001_py39.py new file mode 100644 index 00000000..41a51f44 --- /dev/null +++ b/docs_src/tutorial/fastapi/multiple_models/tutorial001_py39.py @@ -0,0 +1,60 @@ +from typing import Optional + +from fastapi import FastAPI +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 = Field(index=True) + secret_name: str + age: Optional[int] = Field(default=None, index=True) + + +class HeroCreate(SQLModel): + name: str + secret_name: str + age: Optional[int] = None + + +class HeroRead(SQLModel): + id: int + name: str + secret_name: str + age: Optional[int] = None + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +connect_args = {"check_same_thread": False} +engine = create_engine(sqlite_url, echo=True, connect_args=connect_args) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +app = FastAPI() + + +@app.on_event("startup") +def on_startup(): + create_db_and_tables() + + +@app.post("/heroes/", response_model=HeroRead) +def create_hero(hero: HeroCreate): + with Session(engine) as session: + db_hero = Hero.from_orm(hero) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.get("/heroes/", response_model=list[HeroRead]) +def read_heroes(): + with Session(engine) as session: + heroes = session.exec(select(Hero)).all() + return heroes diff --git a/docs_src/tutorial/fastapi/multiple_models/tutorial002_py310.py b/docs_src/tutorial/fastapi/multiple_models/tutorial002_py310.py new file mode 100644 index 00000000..3eda88b1 --- /dev/null +++ b/docs_src/tutorial/fastapi/multiple_models/tutorial002_py310.py @@ -0,0 +1,56 @@ +from fastapi import FastAPI +from sqlmodel import Field, Session, SQLModel, create_engine, select + + +class HeroBase(SQLModel): + name: str = Field(index=True) + secret_name: str + age: int | None = Field(default=None, index=True) + + +class Hero(HeroBase, table=True): + id: int | None = Field(default=None, primary_key=True) + + +class HeroCreate(HeroBase): + pass + + +class HeroRead(HeroBase): + id: int + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +connect_args = {"check_same_thread": False} +engine = create_engine(sqlite_url, echo=True, connect_args=connect_args) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +app = FastAPI() + + +@app.on_event("startup") +def on_startup(): + create_db_and_tables() + + +@app.post("/heroes/", response_model=HeroRead) +def create_hero(hero: HeroCreate): + with Session(engine) as session: + db_hero = Hero.from_orm(hero) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.get("/heroes/", response_model=list[HeroRead]) +def read_heroes(): + with Session(engine) as session: + heroes = session.exec(select(Hero)).all() + return heroes diff --git a/docs_src/tutorial/fastapi/multiple_models/tutorial002_py39.py b/docs_src/tutorial/fastapi/multiple_models/tutorial002_py39.py new file mode 100644 index 00000000..473fe5b8 --- /dev/null +++ b/docs_src/tutorial/fastapi/multiple_models/tutorial002_py39.py @@ -0,0 +1,58 @@ +from typing import Optional + +from fastapi import FastAPI +from sqlmodel import Field, Session, SQLModel, create_engine, select + + +class HeroBase(SQLModel): + name: str = Field(index=True) + secret_name: str + age: Optional[int] = Field(default=None, index=True) + + +class Hero(HeroBase, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + + +class HeroCreate(HeroBase): + pass + + +class HeroRead(HeroBase): + id: int + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +connect_args = {"check_same_thread": False} +engine = create_engine(sqlite_url, echo=True, connect_args=connect_args) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +app = FastAPI() + + +@app.on_event("startup") +def on_startup(): + create_db_and_tables() + + +@app.post("/heroes/", response_model=HeroRead) +def create_hero(hero: HeroCreate): + with Session(engine) as session: + db_hero = Hero.from_orm(hero) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.get("/heroes/", response_model=list[HeroRead]) +def read_heroes(): + with Session(engine) as session: + heroes = session.exec(select(Hero)).all() + return heroes diff --git a/docs_src/tutorial/fastapi/read_one/tutorial001_py310.py b/docs_src/tutorial/fastapi/read_one/tutorial001_py310.py new file mode 100644 index 00000000..8883570d --- /dev/null +++ b/docs_src/tutorial/fastapi/read_one/tutorial001_py310.py @@ -0,0 +1,65 @@ +from fastapi import FastAPI, HTTPException +from sqlmodel import Field, Session, SQLModel, create_engine, select + + +class HeroBase(SQLModel): + name: str = Field(index=True) + secret_name: str + age: int | None = Field(default=None, index=True) + + +class Hero(HeroBase, table=True): + id: int | None = Field(default=None, primary_key=True) + + +class HeroCreate(HeroBase): + pass + + +class HeroRead(HeroBase): + id: int + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +connect_args = {"check_same_thread": False} +engine = create_engine(sqlite_url, echo=True, connect_args=connect_args) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +app = FastAPI() + + +@app.on_event("startup") +def on_startup(): + create_db_and_tables() + + +@app.post("/heroes/", response_model=HeroRead) +def create_hero(hero: HeroCreate): + with Session(engine) as session: + db_hero = Hero.from_orm(hero) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.get("/heroes/", response_model=list[HeroRead]) +def read_heroes(): + with Session(engine) as session: + heroes = session.exec(select(Hero)).all() + return heroes + + +@app.get("/heroes/{hero_id}", response_model=HeroRead) +def read_hero(hero_id: int): + with Session(engine) as session: + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + return hero diff --git a/docs_src/tutorial/fastapi/read_one/tutorial001_py39.py b/docs_src/tutorial/fastapi/read_one/tutorial001_py39.py new file mode 100644 index 00000000..0ad70166 --- /dev/null +++ b/docs_src/tutorial/fastapi/read_one/tutorial001_py39.py @@ -0,0 +1,67 @@ +from typing import Optional + +from fastapi import FastAPI, HTTPException +from sqlmodel import Field, Session, SQLModel, create_engine, select + + +class HeroBase(SQLModel): + name: str = Field(index=True) + secret_name: str + age: Optional[int] = Field(default=None, index=True) + + +class Hero(HeroBase, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + + +class HeroCreate(HeroBase): + pass + + +class HeroRead(HeroBase): + id: int + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +connect_args = {"check_same_thread": False} +engine = create_engine(sqlite_url, echo=True, connect_args=connect_args) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +app = FastAPI() + + +@app.on_event("startup") +def on_startup(): + create_db_and_tables() + + +@app.post("/heroes/", response_model=HeroRead) +def create_hero(hero: HeroCreate): + with Session(engine) as session: + db_hero = Hero.from_orm(hero) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.get("/heroes/", response_model=list[HeroRead]) +def read_heroes(): + with Session(engine) as session: + heroes = session.exec(select(Hero)).all() + return heroes + + +@app.get("/heroes/{hero_id}", response_model=HeroRead) +def read_hero(hero_id: int): + with Session(engine) as session: + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + return hero diff --git a/docs_src/tutorial/fastapi/relationships/tutorial001_py310.py b/docs_src/tutorial/fastapi/relationships/tutorial001_py310.py new file mode 100644 index 00000000..bec6a6f2 --- /dev/null +++ b/docs_src/tutorial/fastapi/relationships/tutorial001_py310.py @@ -0,0 +1,199 @@ +from fastapi import Depends, FastAPI, HTTPException, Query +from sqlmodel import Field, Relationship, Session, SQLModel, create_engine, select + + +class TeamBase(SQLModel): + name: str = Field(index=True) + headquarters: str + + +class Team(TeamBase, table=True): + id: int | None = Field(default=None, primary_key=True) + + heroes: list["Hero"] = Relationship(back_populates="team") + + +class TeamCreate(TeamBase): + pass + + +class TeamRead(TeamBase): + id: int + + +class TeamUpdate(SQLModel): + id: int | None = None + name: str | None = None + headquarters: str | None = None + + +class HeroBase(SQLModel): + 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") + + +class Hero(HeroBase, table=True): + id: int | None = Field(default=None, primary_key=True) + + team: Team | None = Relationship(back_populates="heroes") + + +class HeroRead(HeroBase): + id: int + + +class HeroCreate(HeroBase): + pass + + +class HeroUpdate(SQLModel): + name: str | None = None + secret_name: str | None = None + age: int | None = None + team_id: int | None = None + + +class HeroReadWithTeam(HeroRead): + team: TeamRead | None = None + + +class TeamReadWithHeroes(TeamRead): + heroes: list[HeroRead] = [] + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +connect_args = {"check_same_thread": False} +engine = create_engine(sqlite_url, echo=True, connect_args=connect_args) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +def get_session(): + with Session(engine) as session: + yield session + + +app = FastAPI() + + +@app.on_event("startup") +def on_startup(): + create_db_and_tables() + + +@app.post("/heroes/", response_model=HeroRead) +def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate): + db_hero = Hero.from_orm(hero) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.get("/heroes/", response_model=list[HeroRead]) +def read_heroes( + *, + session: Session = Depends(get_session), + offset: int = 0, + limit: int = Query(default=100, le=100), +): + heroes = session.exec(select(Hero).offset(offset).limit(limit)).all() + return heroes + + +@app.get("/heroes/{hero_id}", response_model=HeroReadWithTeam) +def read_hero(*, session: Session = Depends(get_session), hero_id: int): + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + return hero + + +@app.patch("/heroes/{hero_id}", response_model=HeroRead) +def update_hero( + *, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate +): + db_hero = session.get(Hero, hero_id) + if not db_hero: + raise HTTPException(status_code=404, detail="Hero not found") + hero_data = hero.dict(exclude_unset=True) + for key, value in hero_data.items(): + setattr(db_hero, key, value) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.delete("/heroes/{hero_id}") +def delete_hero(*, session: Session = Depends(get_session), hero_id: int): + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + session.delete(hero) + session.commit() + return {"ok": True} + + +@app.post("/teams/", response_model=TeamRead) +def create_team(*, session: Session = Depends(get_session), team: TeamCreate): + db_team = Team.from_orm(team) + session.add(db_team) + session.commit() + session.refresh(db_team) + return db_team + + +@app.get("/teams/", response_model=list[TeamRead]) +def read_teams( + *, + session: Session = Depends(get_session), + offset: int = 0, + limit: int = Query(default=100, le=100), +): + teams = session.exec(select(Team).offset(offset).limit(limit)).all() + return teams + + +@app.get("/teams/{team_id}", response_model=TeamReadWithHeroes) +def read_team(*, team_id: int, session: Session = Depends(get_session)): + team = session.get(Team, team_id) + if not team: + raise HTTPException(status_code=404, detail="Team not found") + return team + + +@app.patch("/teams/{team_id}", response_model=TeamRead) +def update_team( + *, + session: Session = Depends(get_session), + team_id: int, + team: TeamUpdate, +): + db_team = session.get(Team, team_id) + if not db_team: + raise HTTPException(status_code=404, detail="Team not found") + team_data = team.dict(exclude_unset=True) + for key, value in team_data.items(): + setattr(db_team, key, value) + session.add(db_team) + session.commit() + session.refresh(db_team) + return db_team + + +@app.delete("/teams/{team_id}") +def delete_team(*, session: Session = Depends(get_session), team_id: int): + team = session.get(Team, team_id) + if not team: + raise HTTPException(status_code=404, detail="Team not found") + session.delete(team) + session.commit() + return {"ok": True} diff --git a/docs_src/tutorial/fastapi/relationships/tutorial001_py39.py b/docs_src/tutorial/fastapi/relationships/tutorial001_py39.py new file mode 100644 index 00000000..38939055 --- /dev/null +++ b/docs_src/tutorial/fastapi/relationships/tutorial001_py39.py @@ -0,0 +1,201 @@ +from typing import Optional + +from fastapi import Depends, FastAPI, HTTPException, Query +from sqlmodel import Field, Relationship, Session, SQLModel, create_engine, select + + +class TeamBase(SQLModel): + name: str = Field(index=True) + headquarters: str + + +class Team(TeamBase, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + + heroes: list["Hero"] = Relationship(back_populates="team") + + +class TeamCreate(TeamBase): + pass + + +class TeamRead(TeamBase): + id: int + + +class TeamUpdate(SQLModel): + id: Optional[int] = None + name: Optional[str] = None + headquarters: Optional[str] = None + + +class HeroBase(SQLModel): + 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") + + +class Hero(HeroBase, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + + team: Optional[Team] = Relationship(back_populates="heroes") + + +class HeroRead(HeroBase): + id: int + + +class HeroCreate(HeroBase): + pass + + +class HeroUpdate(SQLModel): + name: Optional[str] = None + secret_name: Optional[str] = None + age: Optional[int] = None + team_id: Optional[int] = None + + +class HeroReadWithTeam(HeroRead): + team: Optional[TeamRead] = None + + +class TeamReadWithHeroes(TeamRead): + heroes: list[HeroRead] = [] + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +connect_args = {"check_same_thread": False} +engine = create_engine(sqlite_url, echo=True, connect_args=connect_args) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +def get_session(): + with Session(engine) as session: + yield session + + +app = FastAPI() + + +@app.on_event("startup") +def on_startup(): + create_db_and_tables() + + +@app.post("/heroes/", response_model=HeroRead) +def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate): + db_hero = Hero.from_orm(hero) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.get("/heroes/", response_model=list[HeroRead]) +def read_heroes( + *, + session: Session = Depends(get_session), + offset: int = 0, + limit: int = Query(default=100, le=100), +): + heroes = session.exec(select(Hero).offset(offset).limit(limit)).all() + return heroes + + +@app.get("/heroes/{hero_id}", response_model=HeroReadWithTeam) +def read_hero(*, session: Session = Depends(get_session), hero_id: int): + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + return hero + + +@app.patch("/heroes/{hero_id}", response_model=HeroRead) +def update_hero( + *, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate +): + db_hero = session.get(Hero, hero_id) + if not db_hero: + raise HTTPException(status_code=404, detail="Hero not found") + hero_data = hero.dict(exclude_unset=True) + for key, value in hero_data.items(): + setattr(db_hero, key, value) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.delete("/heroes/{hero_id}") +def delete_hero(*, session: Session = Depends(get_session), hero_id: int): + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + session.delete(hero) + session.commit() + return {"ok": True} + + +@app.post("/teams/", response_model=TeamRead) +def create_team(*, session: Session = Depends(get_session), team: TeamCreate): + db_team = Team.from_orm(team) + session.add(db_team) + session.commit() + session.refresh(db_team) + return db_team + + +@app.get("/teams/", response_model=list[TeamRead]) +def read_teams( + *, + session: Session = Depends(get_session), + offset: int = 0, + limit: int = Query(default=100, le=100), +): + teams = session.exec(select(Team).offset(offset).limit(limit)).all() + return teams + + +@app.get("/teams/{team_id}", response_model=TeamReadWithHeroes) +def read_team(*, team_id: int, session: Session = Depends(get_session)): + team = session.get(Team, team_id) + if not team: + raise HTTPException(status_code=404, detail="Team not found") + return team + + +@app.patch("/teams/{team_id}", response_model=TeamRead) +def update_team( + *, + session: Session = Depends(get_session), + team_id: int, + team: TeamUpdate, +): + db_team = session.get(Team, team_id) + if not db_team: + raise HTTPException(status_code=404, detail="Team not found") + team_data = team.dict(exclude_unset=True) + for key, value in team_data.items(): + setattr(db_team, key, value) + session.add(db_team) + session.commit() + session.refresh(db_team) + return db_team + + +@app.delete("/teams/{team_id}") +def delete_team(*, session: Session = Depends(get_session), team_id: int): + team = session.get(Team, team_id) + if not team: + raise HTTPException(status_code=404, detail="Team not found") + session.delete(team) + session.commit() + return {"ok": True} diff --git a/docs_src/tutorial/fastapi/response_model/tutorial001_py310.py b/docs_src/tutorial/fastapi/response_model/tutorial001_py310.py new file mode 100644 index 00000000..25825b41 --- /dev/null +++ b/docs_src/tutorial/fastapi/response_model/tutorial001_py310.py @@ -0,0 +1,44 @@ +from fastapi import FastAPI +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 = Field(index=True) + secret_name: str + age: int | None = Field(default=None, index=True) + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +connect_args = {"check_same_thread": False} +engine = create_engine(sqlite_url, echo=True, connect_args=connect_args) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +app = FastAPI() + + +@app.on_event("startup") +def on_startup(): + create_db_and_tables() + + +@app.post("/heroes/", response_model=Hero) +def create_hero(hero: Hero): + with Session(engine) as session: + session.add(hero) + session.commit() + session.refresh(hero) + return hero + + +@app.get("/heroes/", response_model=list[Hero]) +def read_heroes(): + with Session(engine) as session: + heroes = session.exec(select(Hero)).all() + return heroes diff --git a/docs_src/tutorial/fastapi/response_model/tutorial001_py39.py b/docs_src/tutorial/fastapi/response_model/tutorial001_py39.py new file mode 100644 index 00000000..53b701de --- /dev/null +++ b/docs_src/tutorial/fastapi/response_model/tutorial001_py39.py @@ -0,0 +1,46 @@ +from typing import Optional + +from fastapi import FastAPI +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 = Field(index=True) + secret_name: str + age: Optional[int] = Field(default=None, index=True) + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +connect_args = {"check_same_thread": False} +engine = create_engine(sqlite_url, echo=True, connect_args=connect_args) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +app = FastAPI() + + +@app.on_event("startup") +def on_startup(): + create_db_and_tables() + + +@app.post("/heroes/", response_model=Hero) +def create_hero(hero: Hero): + with Session(engine) as session: + session.add(hero) + session.commit() + session.refresh(hero) + return hero + + +@app.get("/heroes/", response_model=list[Hero]) +def read_heroes(): + with Session(engine) as session: + heroes = session.exec(select(Hero)).all() + return heroes diff --git a/docs_src/tutorial/fastapi/session_with_dependency/tutorial001_py310.py b/docs_src/tutorial/fastapi/session_with_dependency/tutorial001_py310.py new file mode 100644 index 00000000..e8615d91 --- /dev/null +++ b/docs_src/tutorial/fastapi/session_with_dependency/tutorial001_py310.py @@ -0,0 +1,104 @@ +from fastapi import Depends, FastAPI, HTTPException, Query +from sqlmodel import Field, Session, SQLModel, create_engine, select + + +class HeroBase(SQLModel): + name: str = Field(index=True) + secret_name: str + age: int | None = Field(default=None, index=True) + + +class Hero(HeroBase, table=True): + id: int | None = Field(default=None, primary_key=True) + + +class HeroCreate(HeroBase): + pass + + +class HeroRead(HeroBase): + id: int + + +class HeroUpdate(SQLModel): + name: str | None = None + secret_name: str | None = None + age: int | None = None + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +connect_args = {"check_same_thread": False} +engine = create_engine(sqlite_url, echo=True, connect_args=connect_args) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +def get_session(): + with Session(engine) as session: + yield session + + +app = FastAPI() + + +@app.on_event("startup") +def on_startup(): + create_db_and_tables() + + +@app.post("/heroes/", response_model=HeroRead) +def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate): + db_hero = Hero.from_orm(hero) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.get("/heroes/", response_model=list[HeroRead]) +def read_heroes( + *, + session: Session = Depends(get_session), + offset: int = 0, + limit: int = Query(default=100, le=100), +): + heroes = session.exec(select(Hero).offset(offset).limit(limit)).all() + return heroes + + +@app.get("/heroes/{hero_id}", response_model=HeroRead) +def read_hero(*, session: Session = Depends(get_session), hero_id: int): + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + return hero + + +@app.patch("/heroes/{hero_id}", response_model=HeroRead) +def update_hero( + *, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate +): + db_hero = session.get(Hero, hero_id) + if not db_hero: + raise HTTPException(status_code=404, detail="Hero not found") + hero_data = hero.dict(exclude_unset=True) + for key, value in hero_data.items(): + setattr(db_hero, key, value) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.delete("/heroes/{hero_id}") +def delete_hero(*, session: Session = Depends(get_session), hero_id: int): + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + session.delete(hero) + session.commit() + return {"ok": True} diff --git a/docs_src/tutorial/fastapi/session_with_dependency/tutorial001_py39.py b/docs_src/tutorial/fastapi/session_with_dependency/tutorial001_py39.py new file mode 100644 index 00000000..9816e70e --- /dev/null +++ b/docs_src/tutorial/fastapi/session_with_dependency/tutorial001_py39.py @@ -0,0 +1,106 @@ +from typing import Optional + +from fastapi import Depends, FastAPI, HTTPException, Query +from sqlmodel import Field, Session, SQLModel, create_engine, select + + +class HeroBase(SQLModel): + name: str = Field(index=True) + secret_name: str + age: Optional[int] = Field(default=None, index=True) + + +class Hero(HeroBase, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + + +class HeroCreate(HeroBase): + pass + + +class HeroRead(HeroBase): + id: int + + +class HeroUpdate(SQLModel): + name: Optional[str] = None + secret_name: Optional[str] = None + age: Optional[int] = None + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +connect_args = {"check_same_thread": False} +engine = create_engine(sqlite_url, echo=True, connect_args=connect_args) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +def get_session(): + with Session(engine) as session: + yield session + + +app = FastAPI() + + +@app.on_event("startup") +def on_startup(): + create_db_and_tables() + + +@app.post("/heroes/", response_model=HeroRead) +def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate): + db_hero = Hero.from_orm(hero) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.get("/heroes/", response_model=list[HeroRead]) +def read_heroes( + *, + session: Session = Depends(get_session), + offset: int = 0, + limit: int = Query(default=100, le=100), +): + heroes = session.exec(select(Hero).offset(offset).limit(limit)).all() + return heroes + + +@app.get("/heroes/{hero_id}", response_model=HeroRead) +def read_hero(*, session: Session = Depends(get_session), hero_id: int): + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + return hero + + +@app.patch("/heroes/{hero_id}", response_model=HeroRead) +def update_hero( + *, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate +): + db_hero = session.get(Hero, hero_id) + if not db_hero: + raise HTTPException(status_code=404, detail="Hero not found") + hero_data = hero.dict(exclude_unset=True) + for key, value in hero_data.items(): + setattr(db_hero, key, value) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.delete("/heroes/{hero_id}") +def delete_hero(*, session: Session = Depends(get_session), hero_id: int): + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + session.delete(hero) + session.commit() + return {"ok": True} diff --git a/docs_src/tutorial/fastapi/simple_hero_api/tutorial001_py310.py b/docs_src/tutorial/fastapi/simple_hero_api/tutorial001_py310.py new file mode 100644 index 00000000..0e113b0f --- /dev/null +++ b/docs_src/tutorial/fastapi/simple_hero_api/tutorial001_py310.py @@ -0,0 +1,44 @@ +from fastapi import FastAPI +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 = Field(index=True) + secret_name: str + age: int | None = Field(default=None, index=True) + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +connect_args = {"check_same_thread": False} +engine = create_engine(sqlite_url, echo=True, connect_args=connect_args) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +app = FastAPI() + + +@app.on_event("startup") +def on_startup(): + create_db_and_tables() + + +@app.post("/heroes/") +def create_hero(hero: Hero): + with Session(engine) as session: + session.add(hero) + session.commit() + session.refresh(hero) + return hero + + +@app.get("/heroes/") +def read_heroes(): + with Session(engine) as session: + heroes = session.exec(select(Hero)).all() + return heroes diff --git a/docs_src/tutorial/fastapi/teams/tutorial001_py310.py b/docs_src/tutorial/fastapi/teams/tutorial001_py310.py new file mode 100644 index 00000000..a9a527df --- /dev/null +++ b/docs_src/tutorial/fastapi/teams/tutorial001_py310.py @@ -0,0 +1,190 @@ +from fastapi import Depends, FastAPI, HTTPException, Query +from sqlmodel import Field, Relationship, Session, SQLModel, create_engine, select + + +class TeamBase(SQLModel): + name: str = Field(index=True) + headquarters: str + + +class Team(TeamBase, table=True): + id: int | None = Field(default=None, primary_key=True) + + heroes: list["Hero"] = Relationship(back_populates="team") + + +class TeamCreate(TeamBase): + pass + + +class TeamRead(TeamBase): + id: int + + +class TeamUpdate(SQLModel): + name: str | None = None + headquarters: str | None = None + + +class HeroBase(SQLModel): + 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") + + +class Hero(HeroBase, table=True): + id: int | None = Field(default=None, primary_key=True) + + team: Team | None = Relationship(back_populates="heroes") + + +class HeroRead(HeroBase): + id: int + + +class HeroCreate(HeroBase): + pass + + +class HeroUpdate(SQLModel): + name: str | None = None + secret_name: str | None = None + age: int | None = None + team_id: int | None = None + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +connect_args = {"check_same_thread": False} +engine = create_engine(sqlite_url, echo=True, connect_args=connect_args) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +def get_session(): + with Session(engine) as session: + yield session + + +app = FastAPI() + + +@app.on_event("startup") +def on_startup(): + create_db_and_tables() + + +@app.post("/heroes/", response_model=HeroRead) +def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate): + db_hero = Hero.from_orm(hero) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.get("/heroes/", response_model=list[HeroRead]) +def read_heroes( + *, + session: Session = Depends(get_session), + offset: int = 0, + limit: int = Query(default=100, le=100), +): + heroes = session.exec(select(Hero).offset(offset).limit(limit)).all() + return heroes + + +@app.get("/heroes/{hero_id}", response_model=HeroRead) +def read_hero(*, session: Session = Depends(get_session), hero_id: int): + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + return hero + + +@app.patch("/heroes/{hero_id}", response_model=HeroRead) +def update_hero( + *, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate +): + db_hero = session.get(Hero, hero_id) + if not db_hero: + raise HTTPException(status_code=404, detail="Hero not found") + hero_data = hero.dict(exclude_unset=True) + for key, value in hero_data.items(): + setattr(db_hero, key, value) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.delete("/heroes/{hero_id}") +def delete_hero(*, session: Session = Depends(get_session), hero_id: int): + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + session.delete(hero) + session.commit() + return {"ok": True} + + +@app.post("/teams/", response_model=TeamRead) +def create_team(*, session: Session = Depends(get_session), team: TeamCreate): + db_team = Team.from_orm(team) + session.add(db_team) + session.commit() + session.refresh(db_team) + return db_team + + +@app.get("/teams/", response_model=list[TeamRead]) +def read_teams( + *, + session: Session = Depends(get_session), + offset: int = 0, + limit: int = Query(default=100, le=100), +): + teams = session.exec(select(Team).offset(offset).limit(limit)).all() + return teams + + +@app.get("/teams/{team_id}", response_model=TeamRead) +def read_team(*, team_id: int, session: Session = Depends(get_session)): + team = session.get(Team, team_id) + if not team: + raise HTTPException(status_code=404, detail="Team not found") + return team + + +@app.patch("/teams/{team_id}", response_model=TeamRead) +def update_team( + *, + session: Session = Depends(get_session), + team_id: int, + team: TeamUpdate, +): + db_team = session.get(Team, team_id) + if not db_team: + raise HTTPException(status_code=404, detail="Team not found") + team_data = team.dict(exclude_unset=True) + for key, value in team_data.items(): + setattr(db_team, key, value) + session.add(db_team) + session.commit() + session.refresh(db_team) + return db_team + + +@app.delete("/teams/{team_id}") +def delete_team(*, session: Session = Depends(get_session), team_id: int): + team = session.get(Team, team_id) + if not team: + raise HTTPException(status_code=404, detail="Team not found") + session.delete(team) + session.commit() + return {"ok": True} diff --git a/docs_src/tutorial/fastapi/teams/tutorial001_py39.py b/docs_src/tutorial/fastapi/teams/tutorial001_py39.py new file mode 100644 index 00000000..1a364289 --- /dev/null +++ b/docs_src/tutorial/fastapi/teams/tutorial001_py39.py @@ -0,0 +1,192 @@ +from typing import Optional + +from fastapi import Depends, FastAPI, HTTPException, Query +from sqlmodel import Field, Relationship, Session, SQLModel, create_engine, select + + +class TeamBase(SQLModel): + name: str = Field(index=True) + headquarters: str + + +class Team(TeamBase, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + + heroes: list["Hero"] = Relationship(back_populates="team") + + +class TeamCreate(TeamBase): + pass + + +class TeamRead(TeamBase): + id: int + + +class TeamUpdate(SQLModel): + name: Optional[str] = None + headquarters: Optional[str] = None + + +class HeroBase(SQLModel): + 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") + + +class Hero(HeroBase, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + + team: Optional[Team] = Relationship(back_populates="heroes") + + +class HeroRead(HeroBase): + id: int + + +class HeroCreate(HeroBase): + pass + + +class HeroUpdate(SQLModel): + name: Optional[str] = None + secret_name: Optional[str] = None + age: Optional[int] = None + team_id: Optional[int] = None + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +connect_args = {"check_same_thread": False} +engine = create_engine(sqlite_url, echo=True, connect_args=connect_args) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +def get_session(): + with Session(engine) as session: + yield session + + +app = FastAPI() + + +@app.on_event("startup") +def on_startup(): + create_db_and_tables() + + +@app.post("/heroes/", response_model=HeroRead) +def create_hero(*, session: Session = Depends(get_session), hero: HeroCreate): + db_hero = Hero.from_orm(hero) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.get("/heroes/", response_model=list[HeroRead]) +def read_heroes( + *, + session: Session = Depends(get_session), + offset: int = 0, + limit: int = Query(default=100, le=100), +): + heroes = session.exec(select(Hero).offset(offset).limit(limit)).all() + return heroes + + +@app.get("/heroes/{hero_id}", response_model=HeroRead) +def read_hero(*, session: Session = Depends(get_session), hero_id: int): + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + return hero + + +@app.patch("/heroes/{hero_id}", response_model=HeroRead) +def update_hero( + *, session: Session = Depends(get_session), hero_id: int, hero: HeroUpdate +): + db_hero = session.get(Hero, hero_id) + if not db_hero: + raise HTTPException(status_code=404, detail="Hero not found") + hero_data = hero.dict(exclude_unset=True) + for key, value in hero_data.items(): + setattr(db_hero, key, value) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.delete("/heroes/{hero_id}") +def delete_hero(*, session: Session = Depends(get_session), hero_id: int): + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + session.delete(hero) + session.commit() + return {"ok": True} + + +@app.post("/teams/", response_model=TeamRead) +def create_team(*, session: Session = Depends(get_session), team: TeamCreate): + db_team = Team.from_orm(team) + session.add(db_team) + session.commit() + session.refresh(db_team) + return db_team + + +@app.get("/teams/", response_model=list[TeamRead]) +def read_teams( + *, + session: Session = Depends(get_session), + offset: int = 0, + limit: int = Query(default=100, le=100), +): + teams = session.exec(select(Team).offset(offset).limit(limit)).all() + return teams + + +@app.get("/teams/{team_id}", response_model=TeamRead) +def read_team(*, team_id: int, session: Session = Depends(get_session)): + team = session.get(Team, team_id) + if not team: + raise HTTPException(status_code=404, detail="Team not found") + return team + + +@app.patch("/teams/{team_id}", response_model=TeamRead) +def update_team( + *, + session: Session = Depends(get_session), + team_id: int, + team: TeamUpdate, +): + db_team = session.get(Team, team_id) + if not db_team: + raise HTTPException(status_code=404, detail="Team not found") + team_data = team.dict(exclude_unset=True) + for key, value in team_data.items(): + setattr(db_team, key, value) + session.add(db_team) + session.commit() + session.refresh(db_team) + return db_team + + +@app.delete("/teams/{team_id}") +def delete_team(*, session: Session = Depends(get_session), team_id: int): + team = session.get(Team, team_id) + if not team: + raise HTTPException(status_code=404, detail="Team not found") + session.delete(team) + session.commit() + return {"ok": True} diff --git a/docs_src/tutorial/fastapi/update/tutorial001_py310.py b/docs_src/tutorial/fastapi/update/tutorial001_py310.py new file mode 100644 index 00000000..79069181 --- /dev/null +++ b/docs_src/tutorial/fastapi/update/tutorial001_py310.py @@ -0,0 +1,86 @@ +from fastapi import FastAPI, HTTPException, Query +from sqlmodel import Field, Session, SQLModel, create_engine, select + + +class HeroBase(SQLModel): + name: str = Field(index=True) + secret_name: str + age: int | None = Field(default=None, index=True) + + +class Hero(HeroBase, table=True): + id: int | None = Field(default=None, primary_key=True) + + +class HeroCreate(HeroBase): + pass + + +class HeroRead(HeroBase): + id: int + + +class HeroUpdate(SQLModel): + name: str | None = None + secret_name: str | None = None + age: int | None = None + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +connect_args = {"check_same_thread": False} +engine = create_engine(sqlite_url, echo=True, connect_args=connect_args) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +app = FastAPI() + + +@app.on_event("startup") +def on_startup(): + create_db_and_tables() + + +@app.post("/heroes/", response_model=HeroRead) +def create_hero(hero: HeroCreate): + with Session(engine) as session: + db_hero = Hero.from_orm(hero) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.get("/heroes/", response_model=list[HeroRead]) +def read_heroes(offset: int = 0, limit: int = Query(default=100, le=100)): + with Session(engine) as session: + heroes = session.exec(select(Hero).offset(offset).limit(limit)).all() + return heroes + + +@app.get("/heroes/{hero_id}", response_model=HeroRead) +def read_hero(hero_id: int): + with Session(engine) as session: + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + return hero + + +@app.patch("/heroes/{hero_id}", response_model=HeroRead) +def update_hero(hero_id: int, hero: HeroUpdate): + with Session(engine) as session: + db_hero = session.get(Hero, hero_id) + if not db_hero: + raise HTTPException(status_code=404, detail="Hero not found") + hero_data = hero.dict(exclude_unset=True) + for key, value in hero_data.items(): + setattr(db_hero, key, value) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero diff --git a/docs_src/tutorial/fastapi/update/tutorial001_py39.py b/docs_src/tutorial/fastapi/update/tutorial001_py39.py new file mode 100644 index 00000000..c788eb1c --- /dev/null +++ b/docs_src/tutorial/fastapi/update/tutorial001_py39.py @@ -0,0 +1,88 @@ +from typing import Optional + +from fastapi import FastAPI, HTTPException, Query +from sqlmodel import Field, Session, SQLModel, create_engine, select + + +class HeroBase(SQLModel): + name: str = Field(index=True) + secret_name: str + age: Optional[int] = Field(default=None, index=True) + + +class Hero(HeroBase, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + + +class HeroCreate(HeroBase): + pass + + +class HeroRead(HeroBase): + id: int + + +class HeroUpdate(SQLModel): + name: Optional[str] = None + secret_name: Optional[str] = None + age: Optional[int] = None + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +connect_args = {"check_same_thread": False} +engine = create_engine(sqlite_url, echo=True, connect_args=connect_args) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +app = FastAPI() + + +@app.on_event("startup") +def on_startup(): + create_db_and_tables() + + +@app.post("/heroes/", response_model=HeroRead) +def create_hero(hero: HeroCreate): + with Session(engine) as session: + db_hero = Hero.from_orm(hero) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.get("/heroes/", response_model=list[HeroRead]) +def read_heroes(offset: int = 0, limit: int = Query(default=100, le=100)): + with Session(engine) as session: + heroes = session.exec(select(Hero).offset(offset).limit(limit)).all() + return heroes + + +@app.get("/heroes/{hero_id}", response_model=HeroRead) +def read_hero(hero_id: int): + with Session(engine) as session: + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + return hero + + +@app.patch("/heroes/{hero_id}", response_model=HeroRead) +def update_hero(hero_id: int, hero: HeroUpdate): + with Session(engine) as session: + db_hero = session.get(Hero, hero_id) + if not db_hero: + raise HTTPException(status_code=404, detail="Hero not found") + hero_data = hero.dict(exclude_unset=True) + for key, value in hero_data.items(): + setattr(db_hero, key, value) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero diff --git a/docs_src/tutorial/indexes/tutorial001_py310.py b/docs_src/tutorial/indexes/tutorial001_py310.py new file mode 100644 index 00000000..115c447b --- /dev/null +++ b/docs_src/tutorial/indexes/tutorial001_py310.py @@ -0,0 +1,49 @@ +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 = Field(index=True) + secret_name: str + age: int | None = Field(default=None, index=True) + + +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).where(Hero.name == "Deadpond") + results = session.exec(statement) + for hero in results: + print(hero) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/indexes/tutorial002_py310.py b/docs_src/tutorial/indexes/tutorial002_py310.py new file mode 100644 index 00000000..c0b7a1e6 --- /dev/null +++ b/docs_src/tutorial/indexes/tutorial002_py310.py @@ -0,0 +1,57 @@ +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 = Field(index=True) + secret_name: str + age: int | None = Field(default=None, index=True) + + +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) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero).where(Hero.age <= 35) + results = session.exec(statement) + for hero in results: + print(hero) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/insert/tutorial001_py310.py b/docs_src/tutorial/insert/tutorial001_py310.py new file mode 100644 index 00000000..72f95ee2 --- /dev/null +++ b/docs_src/tutorial/insert/tutorial001_py310.py @@ -0,0 +1,43 @@ +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() diff --git a/docs_src/tutorial/insert/tutorial002_py310.py b/docs_src/tutorial/insert/tutorial002_py310.py new file mode 100644 index 00000000..266fbb8d --- /dev/null +++ b/docs_src/tutorial/insert/tutorial002_py310.py @@ -0,0 +1,40 @@ +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() diff --git a/docs_src/tutorial/insert/tutorial003_py310.py b/docs_src/tutorial/insert/tutorial003_py310.py new file mode 100644 index 00000000..da94841a --- /dev/null +++ b/docs_src/tutorial/insert/tutorial003_py310.py @@ -0,0 +1,41 @@ +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)! diff --git a/docs_src/tutorial/many_to_many/tutorial001_py310.py b/docs_src/tutorial/many_to_many/tutorial001_py310.py new file mode 100644 index 00000000..5e8f31ca --- /dev/null +++ b/docs_src/tutorial/many_to_many/tutorial001_py310.py @@ -0,0 +1,78 @@ +from sqlmodel import Field, Relationship, Session, SQLModel, create_engine + + +class HeroTeamLink(SQLModel, table=True): + team_id: int | None = Field(default=None, foreign_key="team.id", primary_key=True) + hero_id: int | None = Field(default=None, foreign_key="hero.id", primary_key=True) + + +class Team(SQLModel, table=True): + id: int | None = Field(default=None, primary_key=True) + name: str = Field(index=True) + headquarters: str + + heroes: list["Hero"] = Relationship(back_populates="teams", link_model=HeroTeamLink) + + +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) + + teams: list[Team] = Relationship(back_populates="heroes", link_model=HeroTeamLink) + + +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(): + with Session(engine) as session: + team_preventers = Team(name="Preventers", headquarters="Sharp Tower") + team_z_force = Team(name="Z-Force", headquarters="Sister Margaret’s Bar") + + hero_deadpond = Hero( + name="Deadpond", + secret_name="Dive Wilson", + teams=[team_z_force, team_preventers], + ) + hero_rusty_man = Hero( + name="Rusty-Man", + secret_name="Tommy Sharp", + age=48, + teams=[team_preventers], + ) + hero_spider_boy = Hero( + name="Spider-Boy", secret_name="Pedro Parqueador", teams=[team_preventers] + ) + session.add(hero_deadpond) + session.add(hero_rusty_man) + session.add(hero_spider_boy) + session.commit() + + session.refresh(hero_deadpond) + session.refresh(hero_rusty_man) + session.refresh(hero_spider_boy) + + print("Deadpond:", hero_deadpond) + print("Deadpond teams:", hero_deadpond.teams) + print("Rusty-Man:", hero_rusty_man) + print("Rusty-Man Teams:", hero_rusty_man.teams) + print("Spider-Boy:", hero_spider_boy) + print("Spider-Boy Teams:", hero_spider_boy.teams) + + +def main(): + create_db_and_tables() + create_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/many_to_many/tutorial001_py39.py b/docs_src/tutorial/many_to_many/tutorial001_py39.py new file mode 100644 index 00000000..0d7325a7 --- /dev/null +++ b/docs_src/tutorial/many_to_many/tutorial001_py39.py @@ -0,0 +1,84 @@ +from typing import Optional + +from sqlmodel import Field, Relationship, Session, SQLModel, create_engine + + +class HeroTeamLink(SQLModel, table=True): + team_id: Optional[int] = Field( + default=None, foreign_key="team.id", primary_key=True + ) + hero_id: Optional[int] = Field( + default=None, foreign_key="hero.id", primary_key=True + ) + + +class Team(SQLModel, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + name: str = Field(index=True) + headquarters: str + + heroes: list["Hero"] = Relationship(back_populates="teams", link_model=HeroTeamLink) + + +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) + + teams: list[Team] = Relationship(back_populates="heroes", link_model=HeroTeamLink) + + +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(): + with Session(engine) as session: + team_preventers = Team(name="Preventers", headquarters="Sharp Tower") + team_z_force = Team(name="Z-Force", headquarters="Sister Margaret’s Bar") + + hero_deadpond = Hero( + name="Deadpond", + secret_name="Dive Wilson", + teams=[team_z_force, team_preventers], + ) + hero_rusty_man = Hero( + name="Rusty-Man", + secret_name="Tommy Sharp", + age=48, + teams=[team_preventers], + ) + hero_spider_boy = Hero( + name="Spider-Boy", secret_name="Pedro Parqueador", teams=[team_preventers] + ) + session.add(hero_deadpond) + session.add(hero_rusty_man) + session.add(hero_spider_boy) + session.commit() + + session.refresh(hero_deadpond) + session.refresh(hero_rusty_man) + session.refresh(hero_spider_boy) + + print("Deadpond:", hero_deadpond) + print("Deadpond teams:", hero_deadpond.teams) + print("Rusty-Man:", hero_rusty_man) + print("Rusty-Man Teams:", hero_rusty_man.teams) + print("Spider-Boy:", hero_spider_boy) + print("Spider-Boy Teams:", hero_spider_boy.teams) + + +def main(): + create_db_and_tables() + create_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/many_to_many/tutorial002_py310.py b/docs_src/tutorial/many_to_many/tutorial002_py310.py new file mode 100644 index 00000000..5823a6e7 --- /dev/null +++ b/docs_src/tutorial/many_to_many/tutorial002_py310.py @@ -0,0 +1,101 @@ +from sqlmodel import Field, Relationship, Session, SQLModel, create_engine, select + + +class HeroTeamLink(SQLModel, table=True): + team_id: int | None = Field(default=None, foreign_key="team.id", primary_key=True) + hero_id: int | None = Field(default=None, foreign_key="hero.id", primary_key=True) + + +class Team(SQLModel, table=True): + id: int | None = Field(default=None, primary_key=True) + name: str = Field(index=True) + headquarters: str + + heroes: list["Hero"] = Relationship(back_populates="teams", link_model=HeroTeamLink) + + +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) + + teams: list[Team] = Relationship(back_populates="heroes", link_model=HeroTeamLink) + + +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(): + with Session(engine) as session: + team_preventers = Team(name="Preventers", headquarters="Sharp Tower") + team_z_force = Team(name="Z-Force", headquarters="Sister Margaret’s Bar") + + hero_deadpond = Hero( + name="Deadpond", + secret_name="Dive Wilson", + teams=[team_z_force, team_preventers], + ) + hero_rusty_man = Hero( + name="Rusty-Man", + secret_name="Tommy Sharp", + age=48, + teams=[team_preventers], + ) + hero_spider_boy = Hero( + name="Spider-Boy", secret_name="Pedro Parqueador", teams=[team_preventers] + ) + session.add(hero_deadpond) + session.add(hero_rusty_man) + session.add(hero_spider_boy) + session.commit() + + session.refresh(hero_deadpond) + session.refresh(hero_rusty_man) + session.refresh(hero_spider_boy) + + print("Deadpond:", hero_deadpond) + print("Deadpond teams:", hero_deadpond.teams) + print("Rusty-Man:", hero_rusty_man) + print("Rusty-Man Teams:", hero_rusty_man.teams) + print("Spider-Boy:", hero_spider_boy) + print("Spider-Boy Teams:", hero_spider_boy.teams) + + +def update_heroes(): + with Session(engine) as session: + hero_spider_boy = session.exec( + select(Hero).where(Hero.name == "Spider-Boy") + ).one() + team_z_force = session.exec(select(Team).where(Team.name == "Z-Force")).one() + + team_z_force.heroes.append(hero_spider_boy) + session.add(team_z_force) + session.commit() + + print("Updated Spider-Boy's Teams:", hero_spider_boy.teams) + print("Z-Force heroes:", team_z_force.heroes) + + hero_spider_boy.teams.remove(team_z_force) + session.add(team_z_force) + session.commit() + + print("Reverted Z-Force's heroes:", team_z_force.heroes) + print("Reverted Spider-Boy's teams:", hero_spider_boy.teams) + + +def main(): + create_db_and_tables() + create_heroes() + update_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/many_to_many/tutorial002_py39.py b/docs_src/tutorial/many_to_many/tutorial002_py39.py new file mode 100644 index 00000000..54c5d453 --- /dev/null +++ b/docs_src/tutorial/many_to_many/tutorial002_py39.py @@ -0,0 +1,107 @@ +from typing import Optional + +from sqlmodel import Field, Relationship, Session, SQLModel, create_engine, select + + +class HeroTeamLink(SQLModel, table=True): + team_id: Optional[int] = Field( + default=None, foreign_key="team.id", primary_key=True + ) + hero_id: Optional[int] = Field( + default=None, foreign_key="hero.id", primary_key=True + ) + + +class Team(SQLModel, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + name: str = Field(index=True) + headquarters: str + + heroes: list["Hero"] = Relationship(back_populates="teams", link_model=HeroTeamLink) + + +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) + + teams: list[Team] = Relationship(back_populates="heroes", link_model=HeroTeamLink) + + +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(): + with Session(engine) as session: + team_preventers = Team(name="Preventers", headquarters="Sharp Tower") + team_z_force = Team(name="Z-Force", headquarters="Sister Margaret’s Bar") + + hero_deadpond = Hero( + name="Deadpond", + secret_name="Dive Wilson", + teams=[team_z_force, team_preventers], + ) + hero_rusty_man = Hero( + name="Rusty-Man", + secret_name="Tommy Sharp", + age=48, + teams=[team_preventers], + ) + hero_spider_boy = Hero( + name="Spider-Boy", secret_name="Pedro Parqueador", teams=[team_preventers] + ) + session.add(hero_deadpond) + session.add(hero_rusty_man) + session.add(hero_spider_boy) + session.commit() + + session.refresh(hero_deadpond) + session.refresh(hero_rusty_man) + session.refresh(hero_spider_boy) + + print("Deadpond:", hero_deadpond) + print("Deadpond teams:", hero_deadpond.teams) + print("Rusty-Man:", hero_rusty_man) + print("Rusty-Man Teams:", hero_rusty_man.teams) + print("Spider-Boy:", hero_spider_boy) + print("Spider-Boy Teams:", hero_spider_boy.teams) + + +def update_heroes(): + with Session(engine) as session: + hero_spider_boy = session.exec( + select(Hero).where(Hero.name == "Spider-Boy") + ).one() + team_z_force = session.exec(select(Team).where(Team.name == "Z-Force")).one() + + team_z_force.heroes.append(hero_spider_boy) + session.add(team_z_force) + session.commit() + + print("Updated Spider-Boy's Teams:", hero_spider_boy.teams) + print("Z-Force heroes:", team_z_force.heroes) + + hero_spider_boy.teams.remove(team_z_force) + session.add(team_z_force) + session.commit() + + print("Reverted Z-Force's heroes:", team_z_force.heroes) + print("Reverted Spider-Boy's teams:", hero_spider_boy.teams) + + +def main(): + create_db_and_tables() + create_heroes() + update_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/many_to_many/tutorial003_py310.py b/docs_src/tutorial/many_to_many/tutorial003_py310.py new file mode 100644 index 00000000..b8fa4632 --- /dev/null +++ b/docs_src/tutorial/many_to_many/tutorial003_py310.py @@ -0,0 +1,117 @@ +from sqlmodel import Field, Relationship, Session, SQLModel, create_engine, select + + +class HeroTeamLink(SQLModel, table=True): + team_id: int | None = Field(default=None, foreign_key="team.id", primary_key=True) + hero_id: int | None = Field(default=None, foreign_key="hero.id", primary_key=True) + is_training: bool = False + + team: "Team" = Relationship(back_populates="hero_links") + hero: "Hero" = Relationship(back_populates="team_links") + + +class Team(SQLModel, table=True): + id: int | None = Field(default=None, primary_key=True) + name: str = Field(index=True) + headquarters: str + + hero_links: list[HeroTeamLink] = Relationship(back_populates="team") + + +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_links: list[HeroTeamLink] = Relationship(back_populates="hero") + + +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(): + with Session(engine) as session: + team_preventers = Team(name="Preventers", headquarters="Sharp Tower") + team_z_force = Team(name="Z-Force", headquarters="Sister Margaret’s Bar") + + hero_deadpond = Hero( + name="Deadpond", + secret_name="Dive Wilson", + ) + hero_rusty_man = Hero( + name="Rusty-Man", + secret_name="Tommy Sharp", + age=48, + ) + hero_spider_boy = Hero( + name="Spider-Boy", + secret_name="Pedro Parqueador", + ) + deadpond_team_z_link = HeroTeamLink(team=team_z_force, hero=hero_deadpond) + deadpond_preventers_link = HeroTeamLink( + team=team_preventers, hero=hero_deadpond, is_training=True + ) + spider_boy_preventers_link = HeroTeamLink( + team=team_preventers, hero=hero_spider_boy, is_training=True + ) + rusty_man_preventers_link = HeroTeamLink( + team=team_preventers, hero=hero_rusty_man + ) + + session.add(deadpond_team_z_link) + session.add(deadpond_preventers_link) + session.add(spider_boy_preventers_link) + session.add(rusty_man_preventers_link) + session.commit() + + for link in team_z_force.hero_links: + print("Z-Force hero:", link.hero, "is training:", link.is_training) + + for link in team_preventers.hero_links: + print("Preventers hero:", link.hero, "is training:", link.is_training) + + +def update_heroes(): + with Session(engine) as session: + hero_spider_boy = session.exec( + select(Hero).where(Hero.name == "Spider-Boy") + ).one() + team_z_force = session.exec(select(Team).where(Team.name == "Z-Force")).one() + + spider_boy_z_force_link = HeroTeamLink( + team=team_z_force, hero=hero_spider_boy, is_training=True + ) + team_z_force.hero_links.append(spider_boy_z_force_link) + session.add(team_z_force) + session.commit() + + print("Updated Spider-Boy's Teams:", hero_spider_boy.team_links) + print("Z-Force heroes:", team_z_force.hero_links) + + for link in hero_spider_boy.team_links: + if link.team.name == "Preventers": + link.is_training = False + + session.add(hero_spider_boy) + session.commit() + + for link in hero_spider_boy.team_links: + print("Spider-Boy team:", link.team, "is training:", link.is_training) + + +def main(): + create_db_and_tables() + create_heroes() + update_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/many_to_many/tutorial003_py39.py b/docs_src/tutorial/many_to_many/tutorial003_py39.py new file mode 100644 index 00000000..214228a1 --- /dev/null +++ b/docs_src/tutorial/many_to_many/tutorial003_py39.py @@ -0,0 +1,123 @@ +from typing import Optional + +from sqlmodel import Field, Relationship, Session, SQLModel, create_engine, select + + +class HeroTeamLink(SQLModel, table=True): + team_id: Optional[int] = Field( + default=None, foreign_key="team.id", primary_key=True + ) + hero_id: Optional[int] = Field( + default=None, foreign_key="hero.id", primary_key=True + ) + is_training: bool = False + + team: "Team" = Relationship(back_populates="hero_links") + hero: "Hero" = Relationship(back_populates="team_links") + + +class Team(SQLModel, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + name: str = Field(index=True) + headquarters: str + + hero_links: list[HeroTeamLink] = Relationship(back_populates="team") + + +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_links: list[HeroTeamLink] = Relationship(back_populates="hero") + + +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(): + with Session(engine) as session: + team_preventers = Team(name="Preventers", headquarters="Sharp Tower") + team_z_force = Team(name="Z-Force", headquarters="Sister Margaret’s Bar") + + hero_deadpond = Hero( + name="Deadpond", + secret_name="Dive Wilson", + ) + hero_rusty_man = Hero( + name="Rusty-Man", + secret_name="Tommy Sharp", + age=48, + ) + hero_spider_boy = Hero( + name="Spider-Boy", + secret_name="Pedro Parqueador", + ) + deadpond_team_z_link = HeroTeamLink(team=team_z_force, hero=hero_deadpond) + deadpond_preventers_link = HeroTeamLink( + team=team_preventers, hero=hero_deadpond, is_training=True + ) + spider_boy_preventers_link = HeroTeamLink( + team=team_preventers, hero=hero_spider_boy, is_training=True + ) + rusty_man_preventers_link = HeroTeamLink( + team=team_preventers, hero=hero_rusty_man + ) + + session.add(deadpond_team_z_link) + session.add(deadpond_preventers_link) + session.add(spider_boy_preventers_link) + session.add(rusty_man_preventers_link) + session.commit() + + for link in team_z_force.hero_links: + print("Z-Force hero:", link.hero, "is training:", link.is_training) + + for link in team_preventers.hero_links: + print("Preventers hero:", link.hero, "is training:", link.is_training) + + +def update_heroes(): + with Session(engine) as session: + hero_spider_boy = session.exec( + select(Hero).where(Hero.name == "Spider-Boy") + ).one() + team_z_force = session.exec(select(Team).where(Team.name == "Z-Force")).one() + + spider_boy_z_force_link = HeroTeamLink( + team=team_z_force, hero=hero_spider_boy, is_training=True + ) + team_z_force.hero_links.append(spider_boy_z_force_link) + session.add(team_z_force) + session.commit() + + print("Updated Spider-Boy's Teams:", hero_spider_boy.team_links) + print("Z-Force heroes:", team_z_force.hero_links) + + for link in hero_spider_boy.team_links: + if link.team.name == "Preventers": + link.is_training = False + + session.add(hero_spider_boy) + session.commit() + + for link in hero_spider_boy.team_links: + print("Spider-Boy team:", link.team, "is training:", link.is_training) + + +def main(): + create_db_and_tables() + create_heroes() + update_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/offset_and_limit/tutorial001_py310.py b/docs_src/tutorial/offset_and_limit/tutorial001_py310.py new file mode 100644 index 00000000..931f46e2 --- /dev/null +++ b/docs_src/tutorial/offset_and_limit/tutorial001_py310.py @@ -0,0 +1,57 @@ +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 = Field(index=True) + secret_name: str + age: int | None = Field(default=None, index=True) + + +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) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero).limit(3) + results = session.exec(statement) + heroes = results.all() + print(heroes) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/offset_and_limit/tutorial002_py310.py b/docs_src/tutorial/offset_and_limit/tutorial002_py310.py new file mode 100644 index 00000000..ab5c89b7 --- /dev/null +++ b/docs_src/tutorial/offset_and_limit/tutorial002_py310.py @@ -0,0 +1,57 @@ +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 = Field(index=True) + secret_name: str + age: int | None = Field(default=None, index=True) + + +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) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero).offset(3).limit(3) + results = session.exec(statement) + heroes = results.all() + print(heroes) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/offset_and_limit/tutorial003_py310.py b/docs_src/tutorial/offset_and_limit/tutorial003_py310.py new file mode 100644 index 00000000..5ac24937 --- /dev/null +++ b/docs_src/tutorial/offset_and_limit/tutorial003_py310.py @@ -0,0 +1,57 @@ +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 = Field(index=True) + secret_name: str + age: int | None = Field(default=None, index=True) + + +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) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero).offset(6).limit(3) + results = session.exec(statement) + heroes = results.all() + print(heroes) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/offset_and_limit/tutorial004_py310.py b/docs_src/tutorial/offset_and_limit/tutorial004_py310.py new file mode 100644 index 00000000..c7e7bbb4 --- /dev/null +++ b/docs_src/tutorial/offset_and_limit/tutorial004_py310.py @@ -0,0 +1,57 @@ +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 = Field(index=True) + secret_name: str + age: int | None = Field(default=None, index=True) + + +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) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero).where(Hero.age > 32).offset(1).limit(2) + results = session.exec(statement) + heroes = results.all() + print(heroes) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/one/tutorial001_py310.py b/docs_src/tutorial/one/tutorial001_py310.py new file mode 100644 index 00000000..bb1326de --- /dev/null +++ b/docs_src/tutorial/one/tutorial001_py310.py @@ -0,0 +1,57 @@ +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 = Field(index=True) + secret_name: str + age: int | None = Field(default=None, index=True) + + +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) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero).where(Hero.age <= 35) + results = session.exec(statement) + hero = results.first() + print("Hero:", hero) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/one/tutorial002_py310.py b/docs_src/tutorial/one/tutorial002_py310.py new file mode 100644 index 00000000..b82fcfd8 --- /dev/null +++ b/docs_src/tutorial/one/tutorial002_py310.py @@ -0,0 +1,57 @@ +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 = Field(index=True) + secret_name: str + age: int | None = Field(default=None, index=True) + + +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) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero).where(Hero.age < 25) + results = session.exec(statement) + hero = results.first() + print("Hero:", hero) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/one/tutorial003_py310.py b/docs_src/tutorial/one/tutorial003_py310.py new file mode 100644 index 00000000..f674c8a6 --- /dev/null +++ b/docs_src/tutorial/one/tutorial003_py310.py @@ -0,0 +1,57 @@ +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 = Field(index=True) + secret_name: str + age: int | None = Field(default=None, index=True) + + +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) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero).where(Hero.name == "Deadpond") + results = session.exec(statement) + hero = results.one() + print("Hero:", hero) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/one/tutorial004_py310.py b/docs_src/tutorial/one/tutorial004_py310.py new file mode 100644 index 00000000..e55b5530 --- /dev/null +++ b/docs_src/tutorial/one/tutorial004_py310.py @@ -0,0 +1,57 @@ +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 = Field(index=True) + secret_name: str + age: int | None = Field(default=None, index=True) + + +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) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero).where(Hero.age <= 35) + results = session.exec(statement) + hero = results.one() + print("Hero:", hero) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/one/tutorial005_py310.py b/docs_src/tutorial/one/tutorial005_py310.py new file mode 100644 index 00000000..6c51d8fa --- /dev/null +++ b/docs_src/tutorial/one/tutorial005_py310.py @@ -0,0 +1,57 @@ +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 = Field(index=True) + secret_name: str + age: int | None = Field(default=None, index=True) + + +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) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero).where(Hero.age < 25) + results = session.exec(statement) + hero = results.one() + print("Hero:", hero) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/one/tutorial006_py310.py b/docs_src/tutorial/one/tutorial006_py310.py new file mode 100644 index 00000000..6f9b7371 --- /dev/null +++ b/docs_src/tutorial/one/tutorial006_py310.py @@ -0,0 +1,55 @@ +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 = Field(index=True) + secret_name: str + age: int | None = Field(default=None, index=True) + + +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) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def select_heroes(): + with Session(engine) as session: + hero = session.exec(select(Hero).where(Hero.name == "Deadpond")).one() + print("Hero:", hero) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/one/tutorial007_py310.py b/docs_src/tutorial/one/tutorial007_py310.py new file mode 100644 index 00000000..f065f5ac --- /dev/null +++ b/docs_src/tutorial/one/tutorial007_py310.py @@ -0,0 +1,57 @@ +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 = Field(index=True) + secret_name: str + age: int | None = Field(default=None, index=True) + + +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) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero).where(Hero.id == 1) + results = session.exec(statement) + hero = results.first() + print("Hero:", hero) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/one/tutorial008_py310.py b/docs_src/tutorial/one/tutorial008_py310.py new file mode 100644 index 00000000..af916970 --- /dev/null +++ b/docs_src/tutorial/one/tutorial008_py310.py @@ -0,0 +1,55 @@ +from sqlmodel import Field, Session, SQLModel, create_engine + + +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) + + +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) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def select_heroes(): + with Session(engine) as session: + hero = session.get(Hero, 1) + print("Hero:", hero) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/one/tutorial009_py310.py b/docs_src/tutorial/one/tutorial009_py310.py new file mode 100644 index 00000000..57db99ea --- /dev/null +++ b/docs_src/tutorial/one/tutorial009_py310.py @@ -0,0 +1,55 @@ +from sqlmodel import Field, Session, SQLModel, create_engine + + +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) + + +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) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def select_heroes(): + with Session(engine) as session: + hero = session.get(Hero, 9001) + print("Hero:", hero) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/relationship_attributes/back_populates/tutorial001_py310.py b/docs_src/tutorial/relationship_attributes/back_populates/tutorial001_py310.py new file mode 100644 index 00000000..fb35500a --- /dev/null +++ b/docs_src/tutorial/relationship_attributes/back_populates/tutorial001_py310.py @@ -0,0 +1,141 @@ +from sqlmodel import Field, Relationship, Session, SQLModel, create_engine, select + + +class Team(SQLModel, table=True): + id: int | None = Field(default=None, primary_key=True) + name: str = Field(index=True) + headquarters: str + + heroes: list["Hero"] = Relationship() + + +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") + team: Team | None = Relationship() + + +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(): + with Session(engine) as session: + team_preventers = Team(name="Preventers", headquarters="Sharp Tower") + team_z_force = Team(name="Z-Force", headquarters="Sister Margaret’s Bar") + + hero_deadpond = Hero( + name="Deadpond", secret_name="Dive Wilson", team=team_z_force + ) + hero_rusty_man = Hero( + name="Rusty-Man", secret_name="Tommy Sharp", age=48, team=team_preventers + ) + hero_spider_boy = Hero(name="Spider-Boy", secret_name="Pedro Parqueador") + session.add(hero_deadpond) + session.add(hero_rusty_man) + session.add(hero_spider_boy) + session.commit() + + session.refresh(hero_deadpond) + session.refresh(hero_rusty_man) + session.refresh(hero_spider_boy) + + print("Created hero:", hero_deadpond) + print("Created hero:", hero_rusty_man) + print("Created hero:", hero_spider_boy) + + hero_spider_boy.team = team_preventers + session.add(hero_spider_boy) + session.commit() + session.refresh(hero_spider_boy) + print("Updated hero:", hero_spider_boy) + + hero_black_lion = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_sure_e = Hero(name="Princess Sure-E", secret_name="Sure-E") + team_wakaland = Team( + name="Wakaland", + headquarters="Wakaland Capital City", + heroes=[hero_black_lion, hero_sure_e], + ) + session.add(team_wakaland) + session.commit() + session.refresh(team_wakaland) + print("Team Wakaland:", team_wakaland) + + hero_tarantula = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_dr_weird = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_cap = Hero( + name="Captain North America", secret_name="Esteban Rogelios", age=93 + ) + + team_preventers.heroes.append(hero_tarantula) + team_preventers.heroes.append(hero_dr_weird) + team_preventers.heroes.append(hero_cap) + session.add(team_preventers) + session.commit() + session.refresh(hero_tarantula) + session.refresh(hero_dr_weird) + session.refresh(hero_cap) + print("Preventers new hero:", hero_tarantula) + print("Preventers new hero:", hero_dr_weird) + print("Preventers new hero:", hero_cap) + + +def select_heroes(): + with Session(engine) as session: + statement = select(Team).where(Team.name == "Preventers") + result = session.exec(statement) + team_preventers = result.one() + + print("Preventers heroes:", team_preventers.heroes) + + +def update_heroes(): + with Session(engine) as session: + hero_spider_boy = session.exec( + select(Hero).where(Hero.name == "Spider-Boy") + ).one() + + preventers_team = session.exec( + select(Team).where(Team.name == "Preventers") + ).one() + + print("Hero Spider-Boy:", hero_spider_boy) + print("Preventers Team:", preventers_team) + print("Preventers Team Heroes:", preventers_team.heroes) + + hero_spider_boy.team = None + + print("Spider-Boy without team:", hero_spider_boy) + + print("Preventers Team Heroes again:", preventers_team.heroes) + + session.add(hero_spider_boy) + session.commit() + print("After committing") + + session.refresh(hero_spider_boy) + print("Spider-Boy after commit:", hero_spider_boy) + + print("Preventers Team Heroes after commit:", preventers_team.heroes) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + update_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/relationship_attributes/back_populates/tutorial001_py39.py b/docs_src/tutorial/relationship_attributes/back_populates/tutorial001_py39.py new file mode 100644 index 00000000..a5deae91 --- /dev/null +++ b/docs_src/tutorial/relationship_attributes/back_populates/tutorial001_py39.py @@ -0,0 +1,143 @@ +from typing import Optional + +from sqlmodel import Field, Relationship, Session, SQLModel, create_engine, select + + +class Team(SQLModel, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + name: str = Field(index=True) + headquarters: str + + heroes: list["Hero"] = Relationship() + + +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") + team: Optional[Team] = Relationship() + + +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(): + with Session(engine) as session: + team_preventers = Team(name="Preventers", headquarters="Sharp Tower") + team_z_force = Team(name="Z-Force", headquarters="Sister Margaret’s Bar") + + hero_deadpond = Hero( + name="Deadpond", secret_name="Dive Wilson", team=team_z_force + ) + hero_rusty_man = Hero( + name="Rusty-Man", secret_name="Tommy Sharp", age=48, team=team_preventers + ) + hero_spider_boy = Hero(name="Spider-Boy", secret_name="Pedro Parqueador") + session.add(hero_deadpond) + session.add(hero_rusty_man) + session.add(hero_spider_boy) + session.commit() + + session.refresh(hero_deadpond) + session.refresh(hero_rusty_man) + session.refresh(hero_spider_boy) + + print("Created hero:", hero_deadpond) + print("Created hero:", hero_rusty_man) + print("Created hero:", hero_spider_boy) + + hero_spider_boy.team = team_preventers + session.add(hero_spider_boy) + session.commit() + session.refresh(hero_spider_boy) + print("Updated hero:", hero_spider_boy) + + hero_black_lion = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_sure_e = Hero(name="Princess Sure-E", secret_name="Sure-E") + team_wakaland = Team( + name="Wakaland", + headquarters="Wakaland Capital City", + heroes=[hero_black_lion, hero_sure_e], + ) + session.add(team_wakaland) + session.commit() + session.refresh(team_wakaland) + print("Team Wakaland:", team_wakaland) + + hero_tarantula = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_dr_weird = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_cap = Hero( + name="Captain North America", secret_name="Esteban Rogelios", age=93 + ) + + team_preventers.heroes.append(hero_tarantula) + team_preventers.heroes.append(hero_dr_weird) + team_preventers.heroes.append(hero_cap) + session.add(team_preventers) + session.commit() + session.refresh(hero_tarantula) + session.refresh(hero_dr_weird) + session.refresh(hero_cap) + print("Preventers new hero:", hero_tarantula) + print("Preventers new hero:", hero_dr_weird) + print("Preventers new hero:", hero_cap) + + +def select_heroes(): + with Session(engine) as session: + statement = select(Team).where(Team.name == "Preventers") + result = session.exec(statement) + team_preventers = result.one() + + print("Preventers heroes:", team_preventers.heroes) + + +def update_heroes(): + with Session(engine) as session: + hero_spider_boy = session.exec( + select(Hero).where(Hero.name == "Spider-Boy") + ).one() + + preventers_team = session.exec( + select(Team).where(Team.name == "Preventers") + ).one() + + print("Hero Spider-Boy:", hero_spider_boy) + print("Preventers Team:", preventers_team) + print("Preventers Team Heroes:", preventers_team.heroes) + + hero_spider_boy.team = None + + print("Spider-Boy without team:", hero_spider_boy) + + print("Preventers Team Heroes again:", preventers_team.heroes) + + session.add(hero_spider_boy) + session.commit() + print("After committing") + + session.refresh(hero_spider_boy) + print("Spider-Boy after commit:", hero_spider_boy) + + print("Preventers Team Heroes after commit:", preventers_team.heroes) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + update_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/relationship_attributes/back_populates/tutorial002_py310.py b/docs_src/tutorial/relationship_attributes/back_populates/tutorial002_py310.py new file mode 100644 index 00000000..2113fde1 --- /dev/null +++ b/docs_src/tutorial/relationship_attributes/back_populates/tutorial002_py310.py @@ -0,0 +1,141 @@ +from sqlmodel import Field, Relationship, Session, SQLModel, create_engine, select + + +class Team(SQLModel, table=True): + id: int | None = Field(default=None, primary_key=True) + name: str = Field(index=True) + headquarters: str + + heroes: list["Hero"] = Relationship(back_populates="team") + + +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") + team: Team | None = Relationship(back_populates="heroes") + + +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(): + with Session(engine) as session: + team_preventers = Team(name="Preventers", headquarters="Sharp Tower") + team_z_force = Team(name="Z-Force", headquarters="Sister Margaret’s Bar") + + hero_deadpond = Hero( + name="Deadpond", secret_name="Dive Wilson", team=team_z_force + ) + hero_rusty_man = Hero( + name="Rusty-Man", secret_name="Tommy Sharp", age=48, team=team_preventers + ) + hero_spider_boy = Hero(name="Spider-Boy", secret_name="Pedro Parqueador") + session.add(hero_deadpond) + session.add(hero_rusty_man) + session.add(hero_spider_boy) + session.commit() + + session.refresh(hero_deadpond) + session.refresh(hero_rusty_man) + session.refresh(hero_spider_boy) + + print("Created hero:", hero_deadpond) + print("Created hero:", hero_rusty_man) + print("Created hero:", hero_spider_boy) + + hero_spider_boy.team = team_preventers + session.add(hero_spider_boy) + session.commit() + session.refresh(hero_spider_boy) + print("Updated hero:", hero_spider_boy) + + hero_black_lion = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_sure_e = Hero(name="Princess Sure-E", secret_name="Sure-E") + team_wakaland = Team( + name="Wakaland", + headquarters="Wakaland Capital City", + heroes=[hero_black_lion, hero_sure_e], + ) + session.add(team_wakaland) + session.commit() + session.refresh(team_wakaland) + print("Team Wakaland:", team_wakaland) + + hero_tarantula = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_dr_weird = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_cap = Hero( + name="Captain North America", secret_name="Esteban Rogelios", age=93 + ) + + team_preventers.heroes.append(hero_tarantula) + team_preventers.heroes.append(hero_dr_weird) + team_preventers.heroes.append(hero_cap) + session.add(team_preventers) + session.commit() + session.refresh(hero_tarantula) + session.refresh(hero_dr_weird) + session.refresh(hero_cap) + print("Preventers new hero:", hero_tarantula) + print("Preventers new hero:", hero_dr_weird) + print("Preventers new hero:", hero_cap) + + +def select_heroes(): + with Session(engine) as session: + statement = select(Team).where(Team.name == "Preventers") + result = session.exec(statement) + team_preventers = result.one() + + print("Preventers heroes:", team_preventers.heroes) + + +def update_heroes(): + with Session(engine) as session: + hero_spider_boy = session.exec( + select(Hero).where(Hero.name == "Spider-Boy") + ).one() + + preventers_team = session.exec( + select(Team).where(Team.name == "Preventers") + ).one() + + print("Hero Spider-Boy:", hero_spider_boy) + print("Preventers Team:", preventers_team) + print("Preventers Team Heroes:", preventers_team.heroes) + + hero_spider_boy.team = None + + print("Spider-Boy without team:", hero_spider_boy) + + print("Preventers Team Heroes again:", preventers_team.heroes) + + session.add(hero_spider_boy) + session.commit() + print("After committing") + + session.refresh(hero_spider_boy) + print("Spider-Boy after commit:", hero_spider_boy) + + print("Preventers Team Heroes after commit:", preventers_team.heroes) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + update_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/relationship_attributes/back_populates/tutorial002_py39.py b/docs_src/tutorial/relationship_attributes/back_populates/tutorial002_py39.py new file mode 100644 index 00000000..72a86169 --- /dev/null +++ b/docs_src/tutorial/relationship_attributes/back_populates/tutorial002_py39.py @@ -0,0 +1,143 @@ +from typing import Optional + +from sqlmodel import Field, Relationship, Session, SQLModel, create_engine, select + + +class Team(SQLModel, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + name: str = Field(index=True) + headquarters: str + + heroes: list["Hero"] = Relationship(back_populates="team") + + +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") + team: Optional[Team] = Relationship(back_populates="heroes") + + +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(): + with Session(engine) as session: + team_preventers = Team(name="Preventers", headquarters="Sharp Tower") + team_z_force = Team(name="Z-Force", headquarters="Sister Margaret’s Bar") + + hero_deadpond = Hero( + name="Deadpond", secret_name="Dive Wilson", team=team_z_force + ) + hero_rusty_man = Hero( + name="Rusty-Man", secret_name="Tommy Sharp", age=48, team=team_preventers + ) + hero_spider_boy = Hero(name="Spider-Boy", secret_name="Pedro Parqueador") + session.add(hero_deadpond) + session.add(hero_rusty_man) + session.add(hero_spider_boy) + session.commit() + + session.refresh(hero_deadpond) + session.refresh(hero_rusty_man) + session.refresh(hero_spider_boy) + + print("Created hero:", hero_deadpond) + print("Created hero:", hero_rusty_man) + print("Created hero:", hero_spider_boy) + + hero_spider_boy.team = team_preventers + session.add(hero_spider_boy) + session.commit() + session.refresh(hero_spider_boy) + print("Updated hero:", hero_spider_boy) + + hero_black_lion = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_sure_e = Hero(name="Princess Sure-E", secret_name="Sure-E") + team_wakaland = Team( + name="Wakaland", + headquarters="Wakaland Capital City", + heroes=[hero_black_lion, hero_sure_e], + ) + session.add(team_wakaland) + session.commit() + session.refresh(team_wakaland) + print("Team Wakaland:", team_wakaland) + + hero_tarantula = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_dr_weird = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_cap = Hero( + name="Captain North America", secret_name="Esteban Rogelios", age=93 + ) + + team_preventers.heroes.append(hero_tarantula) + team_preventers.heroes.append(hero_dr_weird) + team_preventers.heroes.append(hero_cap) + session.add(team_preventers) + session.commit() + session.refresh(hero_tarantula) + session.refresh(hero_dr_weird) + session.refresh(hero_cap) + print("Preventers new hero:", hero_tarantula) + print("Preventers new hero:", hero_dr_weird) + print("Preventers new hero:", hero_cap) + + +def select_heroes(): + with Session(engine) as session: + statement = select(Team).where(Team.name == "Preventers") + result = session.exec(statement) + team_preventers = result.one() + + print("Preventers heroes:", team_preventers.heroes) + + +def update_heroes(): + with Session(engine) as session: + hero_spider_boy = session.exec( + select(Hero).where(Hero.name == "Spider-Boy") + ).one() + + preventers_team = session.exec( + select(Team).where(Team.name == "Preventers") + ).one() + + print("Hero Spider-Boy:", hero_spider_boy) + print("Preventers Team:", preventers_team) + print("Preventers Team Heroes:", preventers_team.heroes) + + hero_spider_boy.team = None + + print("Spider-Boy without team:", hero_spider_boy) + + print("Preventers Team Heroes again:", preventers_team.heroes) + + session.add(hero_spider_boy) + session.commit() + print("After committing") + + session.refresh(hero_spider_boy) + print("Spider-Boy after commit:", hero_spider_boy) + + print("Preventers Team Heroes after commit:", preventers_team.heroes) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + update_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/relationship_attributes/back_populates/tutorial003_py310.py b/docs_src/tutorial/relationship_attributes/back_populates/tutorial003_py310.py new file mode 100644 index 00000000..aa850b3f --- /dev/null +++ b/docs_src/tutorial/relationship_attributes/back_populates/tutorial003_py310.py @@ -0,0 +1,57 @@ +from sqlmodel import Field, Relationship, SQLModel, create_engine + + +class Weapon(SQLModel, table=True): + id: int | None = Field(default=None, primary_key=True) + name: str = Field(index=True) + + hero: "Hero" = Relationship(back_populates="weapon") + + +class Power(SQLModel, table=True): + id: int | None = Field(default=None, primary_key=True) + name: str = Field(index=True) + + hero_id: int = Field(foreign_key="hero.id") + hero: "Hero" = Relationship(back_populates="powers") + + +class Team(SQLModel, table=True): + id: int | None = Field(default=None, primary_key=True) + name: str = Field(index=True) + headquarters: str + + heroes: list["Hero"] = Relationship(back_populates="team") + + +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") + team: Team | None = Relationship(back_populates="heroes") + + weapon_id: int | None = Field(default=None, foreign_key="weapon.id") + weapon: Weapon | None = Relationship(back_populates="hero") + + powers: list[Power] = Relationship(back_populates="hero") + + +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() diff --git a/docs_src/tutorial/relationship_attributes/back_populates/tutorial003_py39.py b/docs_src/tutorial/relationship_attributes/back_populates/tutorial003_py39.py new file mode 100644 index 00000000..fc0d08df --- /dev/null +++ b/docs_src/tutorial/relationship_attributes/back_populates/tutorial003_py39.py @@ -0,0 +1,59 @@ +from typing import Optional + +from sqlmodel import Field, Relationship, SQLModel, create_engine + + +class Weapon(SQLModel, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + name: str = Field(index=True) + + hero: "Hero" = Relationship(back_populates="weapon") + + +class Power(SQLModel, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + name: str = Field(index=True) + + hero_id: int = Field(foreign_key="hero.id") + hero: "Hero" = Relationship(back_populates="powers") + + +class Team(SQLModel, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + name: str = Field(index=True) + headquarters: str + + heroes: list["Hero"] = Relationship(back_populates="team") + + +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") + team: Optional[Team] = Relationship(back_populates="heroes") + + weapon_id: Optional[int] = Field(default=None, foreign_key="weapon.id") + weapon: Optional[Weapon] = Relationship(back_populates="hero") + + powers: list[Power] = Relationship(back_populates="hero") + + +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() diff --git a/docs_src/tutorial/relationship_attributes/create_and_update_relationships/tutorial001_py310.py b/docs_src/tutorial/relationship_attributes/create_and_update_relationships/tutorial001_py310.py new file mode 100644 index 00000000..4e031476 --- /dev/null +++ b/docs_src/tutorial/relationship_attributes/create_and_update_relationships/tutorial001_py310.py @@ -0,0 +1,100 @@ +from sqlmodel import Field, Relationship, Session, SQLModel, create_engine + + +class Team(SQLModel, table=True): + id: int | None = Field(default=None, primary_key=True) + name: str = Field(index=True) + headquarters: str + + heroes: list["Hero"] = Relationship(back_populates="team") + + +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") + team: Team | None = Relationship(back_populates="heroes") + + +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(): + with Session(engine) as session: + team_preventers = Team(name="Preventers", headquarters="Sharp Tower") + team_z_force = Team(name="Z-Force", headquarters="Sister Margaret’s Bar") + + hero_deadpond = Hero( + name="Deadpond", secret_name="Dive Wilson", team=team_z_force + ) + hero_rusty_man = Hero( + name="Rusty-Man", secret_name="Tommy Sharp", age=48, team=team_preventers + ) + hero_spider_boy = Hero(name="Spider-Boy", secret_name="Pedro Parqueador") + session.add(hero_deadpond) + session.add(hero_rusty_man) + session.add(hero_spider_boy) + session.commit() + + session.refresh(hero_deadpond) + session.refresh(hero_rusty_man) + session.refresh(hero_spider_boy) + + print("Created hero:", hero_deadpond) + print("Created hero:", hero_rusty_man) + print("Created hero:", hero_spider_boy) + + hero_spider_boy.team = team_preventers + session.add(hero_spider_boy) + session.commit() + session.refresh(hero_spider_boy) + print("Updated hero:", hero_spider_boy) + + hero_black_lion = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_sure_e = Hero(name="Princess Sure-E", secret_name="Sure-E") + team_wakaland = Team( + name="Wakaland", + headquarters="Wakaland Capital City", + heroes=[hero_black_lion, hero_sure_e], + ) + session.add(team_wakaland) + session.commit() + session.refresh(team_wakaland) + print("Team Wakaland:", team_wakaland) + + hero_tarantula = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_dr_weird = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_cap = Hero( + name="Captain North America", secret_name="Esteban Rogelios", age=93 + ) + + team_preventers.heroes.append(hero_tarantula) + team_preventers.heroes.append(hero_dr_weird) + team_preventers.heroes.append(hero_cap) + session.add(team_preventers) + session.commit() + session.refresh(hero_tarantula) + session.refresh(hero_dr_weird) + session.refresh(hero_cap) + print("Preventers new hero:", hero_tarantula) + print("Preventers new hero:", hero_dr_weird) + print("Preventers new hero:", hero_cap) + + +def main(): + create_db_and_tables() + create_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/relationship_attributes/create_and_update_relationships/tutorial001_py39.py b/docs_src/tutorial/relationship_attributes/create_and_update_relationships/tutorial001_py39.py new file mode 100644 index 00000000..bc51058a --- /dev/null +++ b/docs_src/tutorial/relationship_attributes/create_and_update_relationships/tutorial001_py39.py @@ -0,0 +1,102 @@ +from typing import Optional + +from sqlmodel import Field, Relationship, Session, SQLModel, create_engine + + +class Team(SQLModel, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + name: str = Field(index=True) + headquarters: str + + heroes: list["Hero"] = Relationship(back_populates="team") + + +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") + team: Optional[Team] = Relationship(back_populates="heroes") + + +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(): + with Session(engine) as session: + team_preventers = Team(name="Preventers", headquarters="Sharp Tower") + team_z_force = Team(name="Z-Force", headquarters="Sister Margaret’s Bar") + + hero_deadpond = Hero( + name="Deadpond", secret_name="Dive Wilson", team=team_z_force + ) + hero_rusty_man = Hero( + name="Rusty-Man", secret_name="Tommy Sharp", age=48, team=team_preventers + ) + hero_spider_boy = Hero(name="Spider-Boy", secret_name="Pedro Parqueador") + session.add(hero_deadpond) + session.add(hero_rusty_man) + session.add(hero_spider_boy) + session.commit() + + session.refresh(hero_deadpond) + session.refresh(hero_rusty_man) + session.refresh(hero_spider_boy) + + print("Created hero:", hero_deadpond) + print("Created hero:", hero_rusty_man) + print("Created hero:", hero_spider_boy) + + hero_spider_boy.team = team_preventers + session.add(hero_spider_boy) + session.commit() + session.refresh(hero_spider_boy) + print("Updated hero:", hero_spider_boy) + + hero_black_lion = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_sure_e = Hero(name="Princess Sure-E", secret_name="Sure-E") + team_wakaland = Team( + name="Wakaland", + headquarters="Wakaland Capital City", + heroes=[hero_black_lion, hero_sure_e], + ) + session.add(team_wakaland) + session.commit() + session.refresh(team_wakaland) + print("Team Wakaland:", team_wakaland) + + hero_tarantula = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_dr_weird = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_cap = Hero( + name="Captain North America", secret_name="Esteban Rogelios", age=93 + ) + + team_preventers.heroes.append(hero_tarantula) + team_preventers.heroes.append(hero_dr_weird) + team_preventers.heroes.append(hero_cap) + session.add(team_preventers) + session.commit() + session.refresh(hero_tarantula) + session.refresh(hero_dr_weird) + session.refresh(hero_cap) + print("Preventers new hero:", hero_tarantula) + print("Preventers new hero:", hero_dr_weird) + print("Preventers new hero:", hero_cap) + + +def main(): + create_db_and_tables() + create_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/relationship_attributes/define_relationship_attributes/tutorial001_py310.py b/docs_src/tutorial/relationship_attributes/define_relationship_attributes/tutorial001_py310.py new file mode 100644 index 00000000..dc4e261b --- /dev/null +++ b/docs_src/tutorial/relationship_attributes/define_relationship_attributes/tutorial001_py310.py @@ -0,0 +1,68 @@ +from sqlmodel import Field, Relationship, Session, SQLModel, create_engine + + +class Team(SQLModel, table=True): + id: int | None = Field(default=None, primary_key=True) + name: str = Field(index=True) + headquarters: str + + heroes: list["Hero"] = Relationship(back_populates="team") + + +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") + team: Team | None = Relationship(back_populates="heroes") + + +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(): + with Session(engine) as session: + team_preventers = Team(name="Preventers", headquarters="Sharp Tower") + team_z_force = Team(name="Z-Force", headquarters="Sister Margaret’s Bar") + + hero_deadpond = Hero( + name="Deadpond", secret_name="Dive Wilson", team=team_z_force + ) + hero_rusty_man = Hero( + name="Rusty-Man", secret_name="Tommy Sharp", age=48, team=team_preventers + ) + hero_spider_boy = Hero(name="Spider-Boy", secret_name="Pedro Parqueador") + session.add(hero_deadpond) + session.add(hero_rusty_man) + session.add(hero_spider_boy) + session.commit() + + session.refresh(hero_deadpond) + session.refresh(hero_rusty_man) + session.refresh(hero_spider_boy) + + print("Created hero:", hero_deadpond) + print("Created hero:", hero_rusty_man) + print("Created hero:", hero_spider_boy) + + hero_spider_boy.team = team_preventers + session.add(hero_spider_boy) + session.commit() + + +def main(): + create_db_and_tables() + create_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/relationship_attributes/define_relationship_attributes/tutorial001_py39.py b/docs_src/tutorial/relationship_attributes/define_relationship_attributes/tutorial001_py39.py new file mode 100644 index 00000000..42ba84da --- /dev/null +++ b/docs_src/tutorial/relationship_attributes/define_relationship_attributes/tutorial001_py39.py @@ -0,0 +1,70 @@ +from typing import Optional + +from sqlmodel import Field, Relationship, Session, SQLModel, create_engine + + +class Team(SQLModel, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + name: str = Field(index=True) + headquarters: str + + heroes: list["Hero"] = Relationship(back_populates="team") + + +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") + team: Optional[Team] = Relationship(back_populates="heroes") + + +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(): + with Session(engine) as session: + team_preventers = Team(name="Preventers", headquarters="Sharp Tower") + team_z_force = Team(name="Z-Force", headquarters="Sister Margaret’s Bar") + + hero_deadpond = Hero( + name="Deadpond", secret_name="Dive Wilson", team=team_z_force + ) + hero_rusty_man = Hero( + name="Rusty-Man", secret_name="Tommy Sharp", age=48, team=team_preventers + ) + hero_spider_boy = Hero(name="Spider-Boy", secret_name="Pedro Parqueador") + session.add(hero_deadpond) + session.add(hero_rusty_man) + session.add(hero_spider_boy) + session.commit() + + session.refresh(hero_deadpond) + session.refresh(hero_rusty_man) + session.refresh(hero_spider_boy) + + print("Created hero:", hero_deadpond) + print("Created hero:", hero_rusty_man) + print("Created hero:", hero_spider_boy) + + hero_spider_boy.team = team_preventers + session.add(hero_spider_boy) + session.commit() + + +def main(): + create_db_and_tables() + create_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/relationship_attributes/read_relationships/tutorial001_py310.py b/docs_src/tutorial/relationship_attributes/read_relationships/tutorial001_py310.py new file mode 100644 index 00000000..18b3f178 --- /dev/null +++ b/docs_src/tutorial/relationship_attributes/read_relationships/tutorial001_py310.py @@ -0,0 +1,115 @@ +from sqlmodel import Field, Relationship, Session, SQLModel, create_engine, select + + +class Team(SQLModel, table=True): + id: int | None = Field(default=None, primary_key=True) + name: str = Field(index=True) + headquarters: str + + heroes: list["Hero"] = Relationship(back_populates="team") + + +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") + team: Team | None = Relationship(back_populates="heroes") + + +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(): + with Session(engine) as session: + team_preventers = Team(name="Preventers", headquarters="Sharp Tower") + team_z_force = Team(name="Z-Force", headquarters="Sister Margaret’s Bar") + + hero_deadpond = Hero( + name="Deadpond", secret_name="Dive Wilson", team=team_z_force + ) + hero_rusty_man = Hero( + name="Rusty-Man", secret_name="Tommy Sharp", age=48, team=team_preventers + ) + hero_spider_boy = Hero(name="Spider-Boy", secret_name="Pedro Parqueador") + session.add(hero_deadpond) + session.add(hero_rusty_man) + session.add(hero_spider_boy) + session.commit() + + session.refresh(hero_deadpond) + session.refresh(hero_rusty_man) + session.refresh(hero_spider_boy) + + print("Created hero:", hero_deadpond) + print("Created hero:", hero_rusty_man) + print("Created hero:", hero_spider_boy) + + hero_spider_boy.team = team_preventers + session.add(hero_spider_boy) + session.commit() + session.refresh(hero_spider_boy) + print("Updated hero:", hero_spider_boy) + + hero_black_lion = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_sure_e = Hero(name="Princess Sure-E", secret_name="Sure-E") + team_wakaland = Team( + name="Wakaland", + headquarters="Wakaland Capital City", + heroes=[hero_black_lion, hero_sure_e], + ) + session.add(team_wakaland) + session.commit() + session.refresh(team_wakaland) + print("Team Wakaland:", team_wakaland) + + hero_tarantula = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_dr_weird = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_cap = Hero( + name="Captain North America", secret_name="Esteban Rogelios", age=93 + ) + + team_preventers.heroes.append(hero_tarantula) + team_preventers.heroes.append(hero_dr_weird) + team_preventers.heroes.append(hero_cap) + session.add(team_preventers) + session.commit() + session.refresh(hero_tarantula) + session.refresh(hero_dr_weird) + session.refresh(hero_cap) + print("Preventers new hero:", hero_tarantula) + print("Preventers new hero:", hero_dr_weird) + print("Preventers new hero:", hero_cap) + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero).where(Hero.name == "Spider-Boy") + result = session.exec(statement) + hero_spider_boy = result.one() + + statement = select(Team).where(Team.id == hero_spider_boy.team_id) + result = session.exec(statement) + team = result.first() + print("Spider-Boy's team:", team) + + print("Spider-Boy's team again:", hero_spider_boy.team) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/relationship_attributes/read_relationships/tutorial001_py39.py b/docs_src/tutorial/relationship_attributes/read_relationships/tutorial001_py39.py new file mode 100644 index 00000000..18c4c2ed --- /dev/null +++ b/docs_src/tutorial/relationship_attributes/read_relationships/tutorial001_py39.py @@ -0,0 +1,117 @@ +from typing import Optional + +from sqlmodel import Field, Relationship, Session, SQLModel, create_engine, select + + +class Team(SQLModel, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + name: str = Field(index=True) + headquarters: str + + heroes: list["Hero"] = Relationship(back_populates="team") + + +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") + team: Optional[Team] = Relationship(back_populates="heroes") + + +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(): + with Session(engine) as session: + team_preventers = Team(name="Preventers", headquarters="Sharp Tower") + team_z_force = Team(name="Z-Force", headquarters="Sister Margaret’s Bar") + + hero_deadpond = Hero( + name="Deadpond", secret_name="Dive Wilson", team=team_z_force + ) + hero_rusty_man = Hero( + name="Rusty-Man", secret_name="Tommy Sharp", age=48, team=team_preventers + ) + hero_spider_boy = Hero(name="Spider-Boy", secret_name="Pedro Parqueador") + session.add(hero_deadpond) + session.add(hero_rusty_man) + session.add(hero_spider_boy) + session.commit() + + session.refresh(hero_deadpond) + session.refresh(hero_rusty_man) + session.refresh(hero_spider_boy) + + print("Created hero:", hero_deadpond) + print("Created hero:", hero_rusty_man) + print("Created hero:", hero_spider_boy) + + hero_spider_boy.team = team_preventers + session.add(hero_spider_boy) + session.commit() + session.refresh(hero_spider_boy) + print("Updated hero:", hero_spider_boy) + + hero_black_lion = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_sure_e = Hero(name="Princess Sure-E", secret_name="Sure-E") + team_wakaland = Team( + name="Wakaland", + headquarters="Wakaland Capital City", + heroes=[hero_black_lion, hero_sure_e], + ) + session.add(team_wakaland) + session.commit() + session.refresh(team_wakaland) + print("Team Wakaland:", team_wakaland) + + hero_tarantula = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_dr_weird = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_cap = Hero( + name="Captain North America", secret_name="Esteban Rogelios", age=93 + ) + + team_preventers.heroes.append(hero_tarantula) + team_preventers.heroes.append(hero_dr_weird) + team_preventers.heroes.append(hero_cap) + session.add(team_preventers) + session.commit() + session.refresh(hero_tarantula) + session.refresh(hero_dr_weird) + session.refresh(hero_cap) + print("Preventers new hero:", hero_tarantula) + print("Preventers new hero:", hero_dr_weird) + print("Preventers new hero:", hero_cap) + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero).where(Hero.name == "Spider-Boy") + result = session.exec(statement) + hero_spider_boy = result.one() + + statement = select(Team).where(Team.id == hero_spider_boy.team_id) + result = session.exec(statement) + team = result.first() + print("Spider-Boy's team:", team) + + print("Spider-Boy's team again:", hero_spider_boy.team) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/relationship_attributes/read_relationships/tutorial002_py310.py b/docs_src/tutorial/relationship_attributes/read_relationships/tutorial002_py310.py new file mode 100644 index 00000000..f1320024 --- /dev/null +++ b/docs_src/tutorial/relationship_attributes/read_relationships/tutorial002_py310.py @@ -0,0 +1,125 @@ +from sqlmodel import Field, Relationship, Session, SQLModel, create_engine, select + + +class Team(SQLModel, table=True): + id: int | None = Field(default=None, primary_key=True) + name: str = Field(index=True) + headquarters: str + + heroes: list["Hero"] = Relationship(back_populates="team") + + +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") + team: Team | None = Relationship(back_populates="heroes") + + +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(): + with Session(engine) as session: + team_preventers = Team(name="Preventers", headquarters="Sharp Tower") + team_z_force = Team(name="Z-Force", headquarters="Sister Margaret’s Bar") + + hero_deadpond = Hero( + name="Deadpond", secret_name="Dive Wilson", team=team_z_force + ) + hero_rusty_man = Hero( + name="Rusty-Man", secret_name="Tommy Sharp", age=48, team=team_preventers + ) + hero_spider_boy = Hero(name="Spider-Boy", secret_name="Pedro Parqueador") + session.add(hero_deadpond) + session.add(hero_rusty_man) + session.add(hero_spider_boy) + session.commit() + + session.refresh(hero_deadpond) + session.refresh(hero_rusty_man) + session.refresh(hero_spider_boy) + + print("Created hero:", hero_deadpond) + print("Created hero:", hero_rusty_man) + print("Created hero:", hero_spider_boy) + + hero_spider_boy.team = team_preventers + session.add(hero_spider_boy) + session.commit() + session.refresh(hero_spider_boy) + print("Updated hero:", hero_spider_boy) + + hero_black_lion = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_sure_e = Hero(name="Princess Sure-E", secret_name="Sure-E") + team_wakaland = Team( + name="Wakaland", + headquarters="Wakaland Capital City", + heroes=[hero_black_lion, hero_sure_e], + ) + session.add(team_wakaland) + session.commit() + session.refresh(team_wakaland) + print("Team Wakaland:", team_wakaland) + + hero_tarantula = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_dr_weird = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_cap = Hero( + name="Captain North America", secret_name="Esteban Rogelios", age=93 + ) + + team_preventers.heroes.append(hero_tarantula) + team_preventers.heroes.append(hero_dr_weird) + team_preventers.heroes.append(hero_cap) + session.add(team_preventers) + session.commit() + session.refresh(hero_tarantula) + session.refresh(hero_dr_weird) + session.refresh(hero_cap) + print("Preventers new hero:", hero_tarantula) + print("Preventers new hero:", hero_dr_weird) + print("Preventers new hero:", hero_cap) + + +def select_heroes(): + with Session(engine) as session: + statement = select(Team).where(Team.name == "Preventers") + result = session.exec(statement) + team_preventers = result.one() + + print("Preventers heroes:", team_preventers.heroes) + + +def update_heroes(): + with Session(engine) as session: + statement = select(Hero).where(Hero.name == "Spider-Boy") + result = session.exec(statement) + hero_spider_boy = result.one() + + hero_spider_boy.team = None + session.add(hero_spider_boy) + session.commit() + + session.refresh(hero_spider_boy) + print("Spider-Boy without team:", hero_spider_boy) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + update_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/relationship_attributes/read_relationships/tutorial002_py39.py b/docs_src/tutorial/relationship_attributes/read_relationships/tutorial002_py39.py new file mode 100644 index 00000000..33310e7c --- /dev/null +++ b/docs_src/tutorial/relationship_attributes/read_relationships/tutorial002_py39.py @@ -0,0 +1,127 @@ +from typing import Optional + +from sqlmodel import Field, Relationship, Session, SQLModel, create_engine, select + + +class Team(SQLModel, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + name: str = Field(index=True) + headquarters: str + + heroes: list["Hero"] = Relationship(back_populates="team") + + +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") + team: Optional[Team] = Relationship(back_populates="heroes") + + +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(): + with Session(engine) as session: + team_preventers = Team(name="Preventers", headquarters="Sharp Tower") + team_z_force = Team(name="Z-Force", headquarters="Sister Margaret’s Bar") + + hero_deadpond = Hero( + name="Deadpond", secret_name="Dive Wilson", team=team_z_force + ) + hero_rusty_man = Hero( + name="Rusty-Man", secret_name="Tommy Sharp", age=48, team=team_preventers + ) + hero_spider_boy = Hero(name="Spider-Boy", secret_name="Pedro Parqueador") + session.add(hero_deadpond) + session.add(hero_rusty_man) + session.add(hero_spider_boy) + session.commit() + + session.refresh(hero_deadpond) + session.refresh(hero_rusty_man) + session.refresh(hero_spider_boy) + + print("Created hero:", hero_deadpond) + print("Created hero:", hero_rusty_man) + print("Created hero:", hero_spider_boy) + + hero_spider_boy.team = team_preventers + session.add(hero_spider_boy) + session.commit() + session.refresh(hero_spider_boy) + print("Updated hero:", hero_spider_boy) + + hero_black_lion = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_sure_e = Hero(name="Princess Sure-E", secret_name="Sure-E") + team_wakaland = Team( + name="Wakaland", + headquarters="Wakaland Capital City", + heroes=[hero_black_lion, hero_sure_e], + ) + session.add(team_wakaland) + session.commit() + session.refresh(team_wakaland) + print("Team Wakaland:", team_wakaland) + + hero_tarantula = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_dr_weird = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_cap = Hero( + name="Captain North America", secret_name="Esteban Rogelios", age=93 + ) + + team_preventers.heroes.append(hero_tarantula) + team_preventers.heroes.append(hero_dr_weird) + team_preventers.heroes.append(hero_cap) + session.add(team_preventers) + session.commit() + session.refresh(hero_tarantula) + session.refresh(hero_dr_weird) + session.refresh(hero_cap) + print("Preventers new hero:", hero_tarantula) + print("Preventers new hero:", hero_dr_weird) + print("Preventers new hero:", hero_cap) + + +def select_heroes(): + with Session(engine) as session: + statement = select(Team).where(Team.name == "Preventers") + result = session.exec(statement) + team_preventers = result.one() + + print("Preventers heroes:", team_preventers.heroes) + + +def update_heroes(): + with Session(engine) as session: + statement = select(Hero).where(Hero.name == "Spider-Boy") + result = session.exec(statement) + hero_spider_boy = result.one() + + hero_spider_boy.team = None + session.add(hero_spider_boy) + session.commit() + + session.refresh(hero_spider_boy) + print("Spider-Boy without team:", hero_spider_boy) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + update_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/select/tutorial001_py310.py b/docs_src/tutorial/select/tutorial001_py310.py new file mode 100644 index 00000000..29bce282 --- /dev/null +++ b/docs_src/tutorial/select/tutorial001_py310.py @@ -0,0 +1,49 @@ +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() diff --git a/docs_src/tutorial/select/tutorial002_py310.py b/docs_src/tutorial/select/tutorial002_py310.py new file mode 100644 index 00000000..b2f9d4d2 --- /dev/null +++ b/docs_src/tutorial/select/tutorial002_py310.py @@ -0,0 +1,50 @@ +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() diff --git a/docs_src/tutorial/select/tutorial003_py310.py b/docs_src/tutorial/select/tutorial003_py310.py new file mode 100644 index 00000000..836998e2 --- /dev/null +++ b/docs_src/tutorial/select/tutorial003_py310.py @@ -0,0 +1,49 @@ +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() diff --git a/docs_src/tutorial/select/tutorial004_py310.py b/docs_src/tutorial/select/tutorial004_py310.py new file mode 100644 index 00000000..6366d408 --- /dev/null +++ b/docs_src/tutorial/select/tutorial004_py310.py @@ -0,0 +1,47 @@ +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() diff --git a/docs_src/tutorial/update/tutorial001_py310.py b/docs_src/tutorial/update/tutorial001_py310.py new file mode 100644 index 00000000..6cc4f489 --- /dev/null +++ b/docs_src/tutorial/update/tutorial001_py310.py @@ -0,0 +1,63 @@ +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 = Field(index=True) + secret_name: str + age: int | None = Field(default=None, index=True) + + +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) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def update_heroes(): + with Session(engine) as session: + statement = select(Hero).where(Hero.name == "Spider-Boy") + results = session.exec(statement) + hero = results.one() + print("Hero:", hero) + + hero.age = 16 + session.add(hero) + session.commit() + session.refresh(hero) + print("Updated hero:", hero) + + +def main(): + create_db_and_tables() + create_heroes() + update_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/update/tutorial002_py310.py b/docs_src/tutorial/update/tutorial002_py310.py new file mode 100644 index 00000000..64cb6916 --- /dev/null +++ b/docs_src/tutorial/update/tutorial002_py310.py @@ -0,0 +1,63 @@ +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 = Field(index=True) + secret_name: str + age: int | None = Field(default=None, index=True) + + +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) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def update_heroes(): + with Session(engine) as session: + statement = select(Hero).where(Hero.name == "Spider-Boy") # (1)! + results = session.exec(statement) # (2)! + hero = results.one() # (3)! + print("Hero:", hero) # (4)! + + hero.age = 16 # (5)! + session.add(hero) # (6)! + session.commit() # (7)! + session.refresh(hero) # (8)! + print("Updated hero:", hero) # (9)! + + +def main(): + create_db_and_tables() + create_heroes() + update_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/update/tutorial003_py310.py b/docs_src/tutorial/update/tutorial003_py310.py new file mode 100644 index 00000000..f250b071 --- /dev/null +++ b/docs_src/tutorial/update/tutorial003_py310.py @@ -0,0 +1,77 @@ +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 = Field(index=True) + secret_name: str + age: int | None = Field(default=None, index=True) + + +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) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def update_heroes(): + with Session(engine) as session: + statement = select(Hero).where(Hero.name == "Spider-Boy") + results = session.exec(statement) + hero_1 = results.one() + print("Hero 1:", hero_1) + + statement = select(Hero).where(Hero.name == "Captain North America") + results = session.exec(statement) + hero_2 = results.one() + print("Hero 2:", hero_2) + + hero_1.age = 16 + hero_1.name = "Spider-Youngster" + session.add(hero_1) + + hero_2.name = "Captain North America Except Canada" + hero_2.age = 110 + session.add(hero_2) + + session.commit() + session.refresh(hero_1) + session.refresh(hero_2) + + print("Updated hero 1:", hero_1) + print("Updated hero 2:", hero_2) + + +def main(): + create_db_and_tables() + create_heroes() + update_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/update/tutorial004_py310.py b/docs_src/tutorial/update/tutorial004_py310.py new file mode 100644 index 00000000..09e54e1c --- /dev/null +++ b/docs_src/tutorial/update/tutorial004_py310.py @@ -0,0 +1,78 @@ +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 = Field(index=True) + secret_name: str + age: int | None = Field(default=None, index=True) + + +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) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def update_heroes(): + with Session(engine) as session: + statement = select(Hero).where(Hero.name == "Spider-Boy") # (1)! + results = session.exec(statement) # (2)! + hero_1 = results.one() # (3)! + print("Hero 1:", hero_1) # (4)! + + statement = select(Hero).where(Hero.name == "Captain North America") # (5)! + results = session.exec(statement) # (6)! + hero_2 = results.one() # (7)! + print("Hero 2:", hero_2) # (8)! + + hero_1.age = 16 # (9)! + hero_1.name = "Spider-Youngster" # (10)! + session.add(hero_1) # (11)! + + hero_2.name = "Captain North America Except Canada" # (12)! + hero_2.age = 110 # (13)! + session.add(hero_2) # (14)! + + session.commit() # (15)! + session.refresh(hero_1) # (16)! + session.refresh(hero_2) # (17)! + + print("Updated hero 1:", hero_1) # (18)! + print("Updated hero 2:", hero_2) # (19)! + # (20)! + + +def main(): + create_db_and_tables() + create_heroes() + update_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/where/tutorial001_py310.py b/docs_src/tutorial/where/tutorial001_py310.py new file mode 100644 index 00000000..a59e5fc2 --- /dev/null +++ b/docs_src/tutorial/where/tutorial001_py310.py @@ -0,0 +1,49 @@ +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).where(Hero.name == "Deadpond") + results = session.exec(statement) + for hero in results: + print(hero) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/where/tutorial002_py310.py b/docs_src/tutorial/where/tutorial002_py310.py new file mode 100644 index 00000000..5db10c58 --- /dev/null +++ b/docs_src/tutorial/where/tutorial002_py310.py @@ -0,0 +1,49 @@ +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).where(Hero.name != "Deadpond") + results = session.exec(statement) + for hero in results: + print(hero) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/where/tutorial003_py310.py b/docs_src/tutorial/where/tutorial003_py310.py new file mode 100644 index 00000000..c368add4 --- /dev/null +++ b/docs_src/tutorial/where/tutorial003_py310.py @@ -0,0 +1,57 @@ +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) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero).where(Hero.age > 35) + results = session.exec(statement) + for hero in results: + print(hero) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/where/tutorial004_py310.py b/docs_src/tutorial/where/tutorial004_py310.py new file mode 100644 index 00000000..5733b717 --- /dev/null +++ b/docs_src/tutorial/where/tutorial004_py310.py @@ -0,0 +1,57 @@ +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) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero).where(Hero.age >= 35) + results = session.exec(statement) + for hero in results: + print(hero) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/where/tutorial005_py310.py b/docs_src/tutorial/where/tutorial005_py310.py new file mode 100644 index 00000000..52515069 --- /dev/null +++ b/docs_src/tutorial/where/tutorial005_py310.py @@ -0,0 +1,57 @@ +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) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero).where(Hero.age < 35) + results = session.exec(statement) + for hero in results: + print(hero) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/where/tutorial006_py310.py b/docs_src/tutorial/where/tutorial006_py310.py new file mode 100644 index 00000000..a3ab8507 --- /dev/null +++ b/docs_src/tutorial/where/tutorial006_py310.py @@ -0,0 +1,57 @@ +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) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero).where(Hero.age <= 35) + results = session.exec(statement) + for hero in results: + print(hero) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/where/tutorial007_py310.py b/docs_src/tutorial/where/tutorial007_py310.py new file mode 100644 index 00000000..589bc986 --- /dev/null +++ b/docs_src/tutorial/where/tutorial007_py310.py @@ -0,0 +1,57 @@ +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) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero).where(Hero.age >= 35).where(Hero.age < 40) + results = session.exec(statement) + for hero in results: + print(hero) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/where/tutorial008_py310.py b/docs_src/tutorial/where/tutorial008_py310.py new file mode 100644 index 00000000..f32260c9 --- /dev/null +++ b/docs_src/tutorial/where/tutorial008_py310.py @@ -0,0 +1,57 @@ +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) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero).where(Hero.age >= 35, Hero.age < 40) + results = session.exec(statement) + for hero in results: + print(hero) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/where/tutorial009_py310.py b/docs_src/tutorial/where/tutorial009_py310.py new file mode 100644 index 00000000..0681d1c0 --- /dev/null +++ b/docs_src/tutorial/where/tutorial009_py310.py @@ -0,0 +1,57 @@ +from sqlmodel import Field, Session, SQLModel, create_engine, or_, 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) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero).where(or_(Hero.age <= 35, Hero.age > 90)) + results = session.exec(statement) + for hero in results: + print(hero) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/where/tutorial010_py310.py b/docs_src/tutorial/where/tutorial010_py310.py new file mode 100644 index 00000000..a65c47ac --- /dev/null +++ b/docs_src/tutorial/where/tutorial010_py310.py @@ -0,0 +1,57 @@ +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) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero).where((Hero.age <= 35) | (Hero.age > 90)) + results = session.exec(statement) + for hero in results: + print(hero) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/where/tutorial011_py310.py b/docs_src/tutorial/where/tutorial011_py310.py new file mode 100644 index 00000000..73aa4aa8 --- /dev/null +++ b/docs_src/tutorial/where/tutorial011_py310.py @@ -0,0 +1,57 @@ +from sqlmodel import Field, Session, SQLModel, col, 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) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero).where(col(Hero.age) >= 35) + results = session.exec(statement) + for hero in results: + print(hero) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/tests/conftest.py b/tests/conftest.py index 2b8e5fc2..7b2cfcd6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,5 +1,6 @@ import shutil import subprocess +import sys from pathlib import Path from typing import Any, Callable, Dict, List, Union @@ -67,3 +68,9 @@ def get_testing_print_function( calls.append(data) return new_print + + +needs_py39 = pytest.mark.skipif(sys.version_info < (3, 9), reason="requires python3.9+") +needs_py310 = pytest.mark.skipif( + sys.version_info < (3, 10), reason="requires python3.10+" +) diff --git a/tests/test_advanced/test_decimal/test_tutorial001_py310.py b/tests/test_advanced/test_decimal/test_tutorial001_py310.py new file mode 100644 index 00000000..f58ea11a --- /dev/null +++ b/tests/test_advanced/test_decimal/test_tutorial001_py310.py @@ -0,0 +1,45 @@ +from decimal import Decimal +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + +expected_calls = [ + [ + "Hero 1:", + { + "name": "Deadpond", + "age": None, + "id": 1, + "secret_name": "Dive Wilson", + "money": Decimal("1.100"), + }, + ], + [ + "Hero 2:", + { + "name": "Rusty-Man", + "age": 48, + "id": 3, + "secret_name": "Tommy Sharp", + "money": Decimal("2.200"), + }, + ], + ["Total money: 3.300"], +] + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.advanced.decimal import tutorial001_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_automatic_id_none_refresh/test_tutorial001_py310_tutorial002_py310.py b/tests/test_tutorial/test_automatic_id_none_refresh/test_tutorial001_py310_tutorial002_py310.py new file mode 100644 index 00000000..9ffcd8ae --- /dev/null +++ b/tests/test_tutorial/test_automatic_id_none_refresh/test_tutorial001_py310_tutorial002_py310.py @@ -0,0 +1,163 @@ +from typing import Any, Dict, List, Union +from unittest.mock import patch + +from sqlmodel import create_engine + +from tests.conftest import get_testing_print_function, needs_py310 + + +def check_calls(calls: List[List[Union[str, Dict[str, Any]]]]): + assert calls[0] == ["Before interacting with the database"] + assert calls[1] == [ + "Hero 1:", + { + "id": None, + "name": "Deadpond", + "secret_name": "Dive Wilson", + "age": None, + }, + ] + assert calls[2] == [ + "Hero 2:", + { + "id": None, + "name": "Spider-Boy", + "secret_name": "Pedro Parqueador", + "age": None, + }, + ] + assert calls[3] == [ + "Hero 3:", + { + "id": None, + "name": "Rusty-Man", + "secret_name": "Tommy Sharp", + "age": 48, + }, + ] + assert calls[4] == ["After adding to the session"] + assert calls[5] == [ + "Hero 1:", + { + "id": None, + "name": "Deadpond", + "secret_name": "Dive Wilson", + "age": None, + }, + ] + assert calls[6] == [ + "Hero 2:", + { + "id": None, + "name": "Spider-Boy", + "secret_name": "Pedro Parqueador", + "age": None, + }, + ] + assert calls[7] == [ + "Hero 3:", + { + "id": None, + "name": "Rusty-Man", + "secret_name": "Tommy Sharp", + "age": 48, + }, + ] + assert calls[8] == ["After committing the session"] + assert calls[9] == ["Hero 1:", {}] + assert calls[10] == ["Hero 2:", {}] + assert calls[11] == ["Hero 3:", {}] + assert calls[12] == ["After committing the session, show IDs"] + assert calls[13] == ["Hero 1 ID:", 1] + assert calls[14] == ["Hero 2 ID:", 2] + assert calls[15] == ["Hero 3 ID:", 3] + assert calls[16] == ["After committing the session, show names"] + assert calls[17] == ["Hero 1 name:", "Deadpond"] + assert calls[18] == ["Hero 2 name:", "Spider-Boy"] + assert calls[19] == ["Hero 3 name:", "Rusty-Man"] + assert calls[20] == ["After refreshing the heroes"] + assert calls[21] == [ + "Hero 1:", + { + "id": 1, + "name": "Deadpond", + "secret_name": "Dive Wilson", + "age": None, + }, + ] + assert calls[22] == [ + "Hero 2:", + { + "id": 2, + "name": "Spider-Boy", + "secret_name": "Pedro Parqueador", + "age": None, + }, + ] + assert calls[23] == [ + "Hero 3:", + { + "id": 3, + "name": "Rusty-Man", + "secret_name": "Tommy Sharp", + "age": 48, + }, + ] + assert calls[24] == ["After the session closes"] + assert calls[21] == [ + "Hero 1:", + { + "id": 1, + "name": "Deadpond", + "secret_name": "Dive Wilson", + "age": None, + }, + ] + assert calls[22] == [ + "Hero 2:", + { + "id": 2, + "name": "Spider-Boy", + "secret_name": "Pedro Parqueador", + "age": None, + }, + ] + assert calls[23] == [ + "Hero 3:", + { + "id": 3, + "name": "Rusty-Man", + "secret_name": "Tommy Sharp", + "age": 48, + }, + ] + + +@needs_py310 +def test_tutorial_001(clear_sqlmodel): + from docs_src.tutorial.automatic_id_none_refresh import tutorial001_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + check_calls(calls) + + +@needs_py310 +def test_tutorial_002(clear_sqlmodel): + from docs_src.tutorial.automatic_id_none_refresh import tutorial002_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + check_calls(calls) diff --git a/tests/test_tutorial/test_code_structure/test_tutorial001_py310.py b/tests/test_tutorial/test_code_structure/test_tutorial001_py310.py new file mode 100644 index 00000000..31965600 --- /dev/null +++ b/tests/test_tutorial/test_code_structure/test_tutorial001_py310.py @@ -0,0 +1,38 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + +expected_calls = [ + [ + "Created hero:", + { + "id": 1, + "name": "Deadpond", + "age": None, + "secret_name": "Dive Wilson", + "team_id": 1, + }, + ], + [ + "Hero's team:", + {"name": "Z-Force", "headquarters": "Sister Margaret’s Bar", "id": 1}, + ], +] + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.code_structure.tutorial001_py310 import app, database + + database.sqlite_url = "sqlite://" + database.engine = create_engine(database.sqlite_url) + app.engine = database.engine + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + app.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_code_structure/test_tutorial001_py39.py b/tests/test_tutorial/test_code_structure/test_tutorial001_py39.py new file mode 100644 index 00000000..b101dea0 --- /dev/null +++ b/tests/test_tutorial/test_code_structure/test_tutorial001_py39.py @@ -0,0 +1,38 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py39 + +expected_calls = [ + [ + "Created hero:", + { + "id": 1, + "name": "Deadpond", + "age": None, + "secret_name": "Dive Wilson", + "team_id": 1, + }, + ], + [ + "Hero's team:", + {"name": "Z-Force", "headquarters": "Sister Margaret’s Bar", "id": 1}, + ], +] + + +@needs_py39 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.code_structure.tutorial001_py39 import app, database + + database.sqlite_url = "sqlite://" + database.engine = create_engine(database.sqlite_url) + app.engine = database.engine + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + app.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_code_structure/test_tutorial002_py310.py b/tests/test_tutorial/test_code_structure/test_tutorial002_py310.py new file mode 100644 index 00000000..5744aa9e --- /dev/null +++ b/tests/test_tutorial/test_code_structure/test_tutorial002_py310.py @@ -0,0 +1,38 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + +expected_calls = [ + [ + "Created hero:", + { + "id": 1, + "name": "Deadpond", + "age": None, + "secret_name": "Dive Wilson", + "team_id": 1, + }, + ], + [ + "Hero's team:", + {"name": "Z-Force", "headquarters": "Sister Margaret’s Bar", "id": 1}, + ], +] + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.code_structure.tutorial002_py310 import app, database + + database.sqlite_url = "sqlite://" + database.engine = create_engine(database.sqlite_url) + app.engine = database.engine + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + app.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_code_structure/test_tutorial002_py39.py b/tests/test_tutorial/test_code_structure/test_tutorial002_py39.py new file mode 100644 index 00000000..bae15c37 --- /dev/null +++ b/tests/test_tutorial/test_code_structure/test_tutorial002_py39.py @@ -0,0 +1,38 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py39 + +expected_calls = [ + [ + "Created hero:", + { + "id": 1, + "name": "Deadpond", + "age": None, + "secret_name": "Dive Wilson", + "team_id": 1, + }, + ], + [ + "Hero's team:", + {"name": "Z-Force", "headquarters": "Sister Margaret’s Bar", "id": 1}, + ], +] + + +@needs_py39 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.code_structure.tutorial002_py39 import app, database + + database.sqlite_url = "sqlite://" + database.engine = create_engine(database.sqlite_url) + app.engine = database.engine + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + app.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_connect/test_create_connected_tables/test_tutorial001_py310.py b/tests/test_tutorial/test_connect/test_create_connected_tables/test_tutorial001_py310.py new file mode 100644 index 00000000..ec2990eb --- /dev/null +++ b/tests/test_tutorial/test_connect/test_create_connected_tables/test_tutorial001_py310.py @@ -0,0 +1,17 @@ +from sqlalchemy import inspect +from sqlalchemy.engine.reflection import Inspector +from sqlmodel import create_engine + +from ....conftest import needs_py310 + + +@needs_py310 +def test_tutorial001(clear_sqlmodel): + from docs_src.tutorial.connect.create_tables import tutorial001_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + mod.main() + insp: Inspector = inspect(mod.engine) + assert insp.has_table(str(mod.Hero.__tablename__)) + assert insp.has_table(str(mod.Team.__tablename__)) diff --git a/tests/test_tutorial/test_connect/test_delete/test_tutorial001_py310.py b/tests/test_tutorial/test_connect/test_delete/test_tutorial001_py310.py new file mode 100644 index 00000000..edc70b8a --- /dev/null +++ b/tests/test_tutorial/test_connect/test_delete/test_tutorial001_py310.py @@ -0,0 +1,73 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ....conftest import get_testing_print_function, needs_py310 + +expected_calls = [ + [ + "Created hero:", + { + "age": None, + "id": 1, + "secret_name": "Dive Wilson", + "team_id": 2, + "name": "Deadpond", + }, + ], + [ + "Created hero:", + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 1, + "name": "Rusty-Man", + }, + ], + [ + "Created hero:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": None, + "name": "Spider-Boy", + }, + ], + [ + "Updated hero:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 1, + "name": "Spider-Boy", + }, + ], + [ + "No longer Preventer:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": None, + "name": "Spider-Boy", + }, + ], +] + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.connect.delete import tutorial001_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_connect/test_insert/test_tutorial001_py310.py b/tests/test_tutorial/test_connect/test_insert/test_tutorial001_py310.py new file mode 100644 index 00000000..854c0068 --- /dev/null +++ b/tests/test_tutorial/test_connect/test_insert/test_tutorial001_py310.py @@ -0,0 +1,53 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ....conftest import get_testing_print_function, needs_py310 + +expected_calls = [ + [ + "Created hero:", + { + "age": None, + "id": 1, + "secret_name": "Dive Wilson", + "team_id": 2, + "name": "Deadpond", + }, + ], + [ + "Created hero:", + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 1, + "name": "Rusty-Man", + }, + ], + [ + "Created hero:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": None, + "name": "Spider-Boy", + }, + ], +] + + +@needs_py310 +def test_tutorial001(clear_sqlmodel): + from docs_src.tutorial.connect.insert import tutorial001_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_connect/test_select/test_tutorial001_py310_tutorial002_py310.py b/tests/test_tutorial/test_connect/test_select/test_tutorial001_py310_tutorial002_py310.py new file mode 100644 index 00000000..01762f3e --- /dev/null +++ b/tests/test_tutorial/test_connect/test_select/test_tutorial001_py310_tutorial002_py310.py @@ -0,0 +1,92 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ....conftest import get_testing_print_function, needs_py310 + +expected_calls = [ + [ + "Created hero:", + { + "age": None, + "id": 1, + "secret_name": "Dive Wilson", + "team_id": 2, + "name": "Deadpond", + }, + ], + [ + "Created hero:", + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 1, + "name": "Rusty-Man", + }, + ], + [ + "Created hero:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": None, + "name": "Spider-Boy", + }, + ], + [ + "Hero:", + { + "age": None, + "id": 1, + "secret_name": "Dive Wilson", + "team_id": 2, + "name": "Deadpond", + }, + "Team:", + {"id": 2, "name": "Z-Force", "headquarters": "Sister Margaret’s Bar"}, + ], + [ + "Hero:", + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 1, + "name": "Rusty-Man", + }, + "Team:", + {"id": 1, "name": "Preventers", "headquarters": "Sharp Tower"}, + ], +] + + +@needs_py310 +def test_tutorial001(clear_sqlmodel): + from docs_src.tutorial.connect.select import tutorial001_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls + + +@needs_py310 +def test_tutorial002(clear_sqlmodel): + from docs_src.tutorial.connect.select import tutorial002_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_connect/test_select/test_tutorial003_py310.py b/tests/test_tutorial/test_connect/test_select/test_tutorial003_py310.py new file mode 100644 index 00000000..69abccb0 --- /dev/null +++ b/tests/test_tutorial/test_connect/test_select/test_tutorial003_py310.py @@ -0,0 +1,89 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ....conftest import get_testing_print_function, needs_py310 + +expected_calls = [ + [ + "Created hero:", + { + "age": None, + "id": 1, + "secret_name": "Dive Wilson", + "team_id": 2, + "name": "Deadpond", + }, + ], + [ + "Created hero:", + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 1, + "name": "Rusty-Man", + }, + ], + [ + "Created hero:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": None, + "name": "Spider-Boy", + }, + ], + [ + "Hero:", + { + "age": None, + "id": 1, + "secret_name": "Dive Wilson", + "team_id": 2, + "name": "Deadpond", + }, + "Team:", + {"id": 2, "name": "Z-Force", "headquarters": "Sister Margaret’s Bar"}, + ], + [ + "Hero:", + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 1, + "name": "Rusty-Man", + }, + "Team:", + {"id": 1, "name": "Preventers", "headquarters": "Sharp Tower"}, + ], + [ + "Hero:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": None, + "name": "Spider-Boy", + }, + "Team:", + None, + ], +] + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.connect.select import tutorial003_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_connect/test_select/test_tutorial004_py310.py b/tests/test_tutorial/test_connect/test_select/test_tutorial004_py310.py new file mode 100644 index 00000000..72974ec6 --- /dev/null +++ b/tests/test_tutorial/test_connect/test_select/test_tutorial004_py310.py @@ -0,0 +1,63 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ....conftest import get_testing_print_function, needs_py310 + +expected_calls = [ + [ + "Created hero:", + { + "age": None, + "id": 1, + "secret_name": "Dive Wilson", + "team_id": 2, + "name": "Deadpond", + }, + ], + [ + "Created hero:", + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 1, + "name": "Rusty-Man", + }, + ], + [ + "Created hero:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": None, + "name": "Spider-Boy", + }, + ], + [ + "Preventer Hero:", + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 1, + "name": "Rusty-Man", + }, + ], +] + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.connect.select import tutorial004_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_connect/test_select/test_tutorial005_py310.py b/tests/test_tutorial/test_connect/test_select/test_tutorial005_py310.py new file mode 100644 index 00000000..a7332c18 --- /dev/null +++ b/tests/test_tutorial/test_connect/test_select/test_tutorial005_py310.py @@ -0,0 +1,65 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ....conftest import get_testing_print_function, needs_py310 + +expected_calls = [ + [ + "Created hero:", + { + "age": None, + "id": 1, + "secret_name": "Dive Wilson", + "team_id": 2, + "name": "Deadpond", + }, + ], + [ + "Created hero:", + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 1, + "name": "Rusty-Man", + }, + ], + [ + "Created hero:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": None, + "name": "Spider-Boy", + }, + ], + [ + "Preventer Hero:", + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 1, + "name": "Rusty-Man", + }, + "Team:", + {"id": 1, "name": "Preventers", "headquarters": "Sharp Tower"}, + ], +] + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.connect.select import tutorial005_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_connect/test_update/test_tutorial001_py310.py b/tests/test_tutorial/test_connect/test_update/test_tutorial001_py310.py new file mode 100644 index 00000000..f3702654 --- /dev/null +++ b/tests/test_tutorial/test_connect/test_update/test_tutorial001_py310.py @@ -0,0 +1,63 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ....conftest import get_testing_print_function, needs_py310 + +expected_calls = [ + [ + "Created hero:", + { + "age": None, + "id": 1, + "secret_name": "Dive Wilson", + "team_id": 2, + "name": "Deadpond", + }, + ], + [ + "Created hero:", + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 1, + "name": "Rusty-Man", + }, + ], + [ + "Created hero:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": None, + "name": "Spider-Boy", + }, + ], + [ + "Updated hero:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 1, + "name": "Spider-Boy", + }, + ], +] + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.connect.update import tutorial001_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_create_db_and_table/test_tutorial001_py310.py b/tests/test_tutorial/test_create_db_and_table/test_tutorial001_py310.py new file mode 100644 index 00000000..465b9f9d --- /dev/null +++ b/tests/test_tutorial/test_create_db_and_table/test_tutorial001_py310.py @@ -0,0 +1,19 @@ +from pathlib import Path + +from ...conftest import coverage_run, needs_py310 + + +@needs_py310 +def test_create_db_and_table(cov_tmp_path: Path): + module = "docs_src.tutorial.create_db_and_table.tutorial001_py310" + result = coverage_run(module=module, cwd=cov_tmp_path) + assert "BEGIN" in result.stdout + assert 'PRAGMA main.table_info("hero")' in result.stdout + assert "CREATE TABLE hero (" in result.stdout + assert "id INTEGER NOT NULL," in result.stdout + assert "name VARCHAR NOT NULL," in result.stdout + assert "secret_name VARCHAR NOT NULL," in result.stdout + assert "age INTEGER," in result.stdout + assert "PRIMARY KEY (id)" in result.stdout + assert ")" in result.stdout + assert "COMMIT" in result.stdout diff --git a/tests/test_tutorial/test_create_db_and_table/test_tutorial002_py310.py b/tests/test_tutorial/test_create_db_and_table/test_tutorial002_py310.py new file mode 100644 index 00000000..3ca3186b --- /dev/null +++ b/tests/test_tutorial/test_create_db_and_table/test_tutorial002_py310.py @@ -0,0 +1,16 @@ +from sqlalchemy import inspect +from sqlalchemy.engine.reflection import Inspector +from sqlmodel import create_engine + +from ...conftest import needs_py310 + + +@needs_py310 +def test_create_db_and_table(clear_sqlmodel): + from docs_src.tutorial.create_db_and_table import tutorial002_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + mod.create_db_and_tables() + insp: Inspector = inspect(mod.engine) + assert insp.has_table(str(mod.Hero.__tablename__)) diff --git a/tests/test_tutorial/test_create_db_and_table/test_tutorial003_py310.py b/tests/test_tutorial/test_create_db_and_table/test_tutorial003_py310.py new file mode 100644 index 00000000..a1806ce2 --- /dev/null +++ b/tests/test_tutorial/test_create_db_and_table/test_tutorial003_py310.py @@ -0,0 +1,16 @@ +from sqlalchemy import inspect +from sqlalchemy.engine.reflection import Inspector +from sqlmodel import create_engine + +from ...conftest import needs_py310 + + +@needs_py310 +def test_create_db_and_table(clear_sqlmodel): + from docs_src.tutorial.create_db_and_table import tutorial003_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + mod.create_db_and_tables() + insp: Inspector = inspect(mod.engine) + assert insp.has_table(str(mod.Hero.__tablename__)) diff --git a/tests/test_tutorial/test_delete/test_tutorial001_py310_tutorial002_py310.py b/tests/test_tutorial/test_delete/test_tutorial001_py310_tutorial002_py310.py new file mode 100644 index 00000000..0f97e748 --- /dev/null +++ b/tests/test_tutorial/test_delete/test_tutorial001_py310_tutorial002_py310.py @@ -0,0 +1,88 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + +expected_calls = [ + [ + "Hero 1:", + {"id": 2, "name": "Spider-Boy", "secret_name": "Pedro Parqueador", "age": None}, + ], + [ + "Hero 2:", + { + "id": 7, + "name": "Captain North America", + "secret_name": "Esteban Rogelios", + "age": 93, + }, + ], + [ + "Updated hero 1:", + { + "id": 2, + "name": "Spider-Youngster", + "secret_name": "Pedro Parqueador", + "age": 16, + }, + ], + [ + "Updated hero 2:", + { + "id": 7, + "name": "Captain North America Except Canada", + "secret_name": "Esteban Rogelios", + "age": 110, + }, + ], + [ + "Hero: ", + { + "id": 2, + "name": "Spider-Youngster", + "secret_name": "Pedro Parqueador", + "age": 16, + }, + ], + [ + "Deleted hero:", + { + "id": 2, + "name": "Spider-Youngster", + "secret_name": "Pedro Parqueador", + "age": 16, + }, + ], + ["There's no hero named Spider-Youngster"], +] + + +@needs_py310 +def test_tutorial001(clear_sqlmodel): + from docs_src.tutorial.delete import tutorial001_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls + + +@needs_py310 +def test_tutorial002(clear_sqlmodel): + from docs_src.tutorial.delete import tutorial002_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_fastapi/test_app_testing/test_tutorial001_py310_tests_main.py b/tests/test_tutorial/test_fastapi/test_app_testing/test_tutorial001_py310_tests_main.py new file mode 100644 index 00000000..781de7c7 --- /dev/null +++ b/tests/test_tutorial/test_fastapi/test_app_testing/test_tutorial001_py310_tests_main.py @@ -0,0 +1,25 @@ +import subprocess +from pathlib import Path + +from ....conftest import needs_py310 + + +@needs_py310 +def test_run_tests(clear_sqlmodel): + from docs_src.tutorial.fastapi.app_testing.tutorial001_py310 import test_main as mod + + test_path = Path(mod.__file__).resolve().parent + top_level_path = Path(__file__).resolve().parent.parent.parent.parent.parent + result = subprocess.run( + [ + "coverage", + "run", + "--parallel-mode", + "-m", + "pytest", + test_path, + ], + cwd=top_level_path, + capture_output=True, + ) + assert result.returncode == 0, result.stdout.decode("utf-8") diff --git a/tests/test_tutorial/test_fastapi/test_app_testing/test_tutorial001_py39_tests_main.py b/tests/test_tutorial/test_fastapi/test_app_testing/test_tutorial001_py39_tests_main.py new file mode 100644 index 00000000..6dbcc80d --- /dev/null +++ b/tests/test_tutorial/test_fastapi/test_app_testing/test_tutorial001_py39_tests_main.py @@ -0,0 +1,25 @@ +import subprocess +from pathlib import Path + +from ....conftest import needs_py39 + + +@needs_py39 +def test_run_tests(clear_sqlmodel): + from docs_src.tutorial.fastapi.app_testing.tutorial001_py39 import test_main as mod + + test_path = Path(mod.__file__).resolve().parent + top_level_path = Path(__file__).resolve().parent.parent.parent.parent.parent + result = subprocess.run( + [ + "coverage", + "run", + "--parallel-mode", + "-m", + "pytest", + test_path, + ], + cwd=top_level_path, + capture_output=True, + ) + assert result.returncode == 0, result.stdout.decode("utf-8") diff --git a/tests/test_tutorial/test_fastapi/test_app_testing/test_tutorial001_tests_main.py b/tests/test_tutorial/test_fastapi/test_app_testing/test_tutorial001_tests_main.py index 8051c7ac..d7c1329b 100644 --- a/tests/test_tutorial/test_fastapi/test_app_testing/test_tutorial001_tests_main.py +++ b/tests/test_tutorial/test_fastapi/test_app_testing/test_tutorial001_tests_main.py @@ -1,83 +1,22 @@ -import importlib - -import pytest -from fastapi.testclient import TestClient -from sqlalchemy import inspect -from sqlalchemy.engine.reflection import Inspector -from sqlmodel import Session, create_engine - -from docs_src.tutorial.fastapi.app_testing.tutorial001 import main as app_mod -from docs_src.tutorial.fastapi.app_testing.tutorial001 import test_main as test_mod -from docs_src.tutorial.fastapi.app_testing.tutorial001.test_main import ( - client_fixture, - session_fixture, -) - -assert session_fixture, "This keeps the session fixture used below" -assert client_fixture, "This keeps the client fixture used below" - - -@pytest.fixture(name="prepare", autouse=True) -def prepare_fixture(clear_sqlmodel): - # Trigger side effects of registering table models in SQLModel - # This has to be called after clear_sqlmodel, but before the session_fixture - # That's why the extra custom fixture here - importlib.reload(app_mod) - importlib.reload(test_mod) - - -def test_create_hero(session: Session, client: TestClient): - test_mod.test_create_hero(client) - - -def test_create_hero_incomplete(session: Session, client: TestClient): - test_mod.test_create_hero_incomplete(client) - - -def test_create_hero_invalid(session: Session, client: TestClient): - test_mod.test_create_hero_invalid(client) - - -def test_read_heroes(session: Session, client: TestClient): - test_mod.test_read_heroes(session=session, client=client) - - -def test_read_hero(session: Session, client: TestClient): - test_mod.test_read_hero(session=session, client=client) - - -def test_update_hero(session: Session, client: TestClient): - test_mod.test_update_hero(session=session, client=client) - - -def test_delete_hero(session: Session, client: TestClient): - test_mod.test_delete_hero(session=session, client=client) - - -def test_startup(): - app_mod.engine = create_engine("sqlite://") - app_mod.on_startup() - insp: Inspector = inspect(app_mod.engine) - assert insp.has_table(str(app_mod.Hero.__tablename__)) - - -def test_get_session(): - app_mod.engine = create_engine("sqlite://") - for session in app_mod.get_session(): - assert isinstance(session, Session) - assert session.bind == app_mod.engine - - -def test_read_hero_not_found(client: TestClient): - response = client.get("/heroes/9000") - assert response.status_code == 404 - - -def test_update_hero_not_found(client: TestClient): - response = client.patch("/heroes/9000", json={"name": "Very-Rusty-Man"}) - assert response.status_code == 404 - - -def test_delete_hero_not_found(client: TestClient): - response = client.delete("/heroes/9000") - assert response.status_code == 404 +import subprocess +from pathlib import Path + + +def test_run_tests(clear_sqlmodel): + from docs_src.tutorial.fastapi.app_testing.tutorial001 import test_main as mod + + test_path = Path(mod.__file__).resolve().parent + top_level_path = Path(__file__).resolve().parent.parent.parent.parent.parent + result = subprocess.run( + [ + "coverage", + "run", + "--parallel-mode", + "-m", + "pytest", + test_path, + ], + cwd=top_level_path, + capture_output=True, + ) + assert result.returncode == 0, result.stdout.decode("utf-8") diff --git a/tests/test_tutorial/test_fastapi/test_delete/test_tutorial001_py310.py b/tests/test_tutorial/test_fastapi/test_delete/test_tutorial001_py310.py new file mode 100644 index 00000000..133b2876 --- /dev/null +++ b/tests/test_tutorial/test_fastapi/test_delete/test_tutorial001_py310.py @@ -0,0 +1,331 @@ +from fastapi.testclient import TestClient +from sqlmodel import create_engine +from sqlmodel.pool import StaticPool + +from ....conftest import needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.fastapi.delete import tutorial001_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine( + mod.sqlite_url, connect_args=mod.connect_args, poolclass=StaticPool + ) + + with TestClient(mod.app) as client: + hero1_data = {"name": "Deadpond", "secret_name": "Dive Wilson"} + hero2_data = { + "name": "Spider-Boy", + "secret_name": "Pedro Parqueador", + "id": 9000, + } + hero3_data = { + "name": "Rusty-Man", + "secret_name": "Tommy Sharp", + "age": 48, + } + response = client.post("/heroes/", json=hero1_data) + assert response.status_code == 200, response.text + response = client.post("/heroes/", json=hero2_data) + assert response.status_code == 200, response.text + hero2 = response.json() + hero2_id = hero2["id"] + response = client.post("/heroes/", json=hero3_data) + assert response.status_code == 200, response.text + response = client.get(f"/heroes/{hero2_id}") + assert response.status_code == 200, response.text + response = client.get("/heroes/9000") + assert response.status_code == 404, response.text + response = client.get("/heroes/") + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 3 + response = client.patch( + f"/heroes/{hero2_id}", json={"secret_name": "Spider-Youngster"} + ) + assert response.status_code == 200, response.text + response = client.patch("/heroes/9001", json={"name": "Dragon Cube X"}) + assert response.status_code == 404, response.text + + response = client.delete(f"/heroes/{hero2_id}") + assert response.status_code == 200, response.text + response = client.get("/heroes/") + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 2 + + response = client.delete("/heroes/9000") + assert response.status_code == 404, response.text + + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/heroes/": { + "get": { + "summary": "Read Heroes", + "operationId": "read_heroes_heroes__get", + "parameters": [ + { + "required": False, + "schema": { + "title": "Offset", + "type": "integer", + "default": 0, + }, + "name": "offset", + "in": "query", + }, + { + "required": False, + "schema": { + "title": "Limit", + "maximum": 100.0, + "type": "integer", + "default": 100, + }, + "name": "limit", + "in": "query", + }, + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "title": "Response Read Heroes Heroes Get", + "type": "array", + "items": { + "$ref": "#/components/schemas/HeroRead" + }, + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "post": { + "summary": "Create Hero", + "operationId": "create_hero_heroes__post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroCreate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + "/heroes/{hero_id}": { + "get": { + "summary": "Read Hero", + "operationId": "read_hero_heroes__hero_id__get", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "delete": { + "summary": "Delete Hero", + "operationId": "delete_hero_heroes__hero_id__delete", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "patch": { + "summary": "Update Hero", + "operationId": "update_hero_heroes__hero_id__patch", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroUpdate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + }, + "components": { + "schemas": { + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + } + }, + }, + "HeroCreate": { + "title": "HeroCreate", + "required": ["name", "secret_name"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + }, + }, + "HeroRead": { + "title": "HeroRead", + "required": ["name", "secret_name", "id"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + "id": {"title": "Id", "type": "integer"}, + }, + }, + "HeroUpdate": { + "title": "HeroUpdate", + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + } + }, + } diff --git a/tests/test_tutorial/test_fastapi/test_delete/test_tutorial001_py39.py b/tests/test_tutorial/test_fastapi/test_delete/test_tutorial001_py39.py new file mode 100644 index 00000000..5aac8cb1 --- /dev/null +++ b/tests/test_tutorial/test_fastapi/test_delete/test_tutorial001_py39.py @@ -0,0 +1,331 @@ +from fastapi.testclient import TestClient +from sqlmodel import create_engine +from sqlmodel.pool import StaticPool + +from ....conftest import needs_py39 + + +@needs_py39 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.fastapi.delete import tutorial001_py39 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine( + mod.sqlite_url, connect_args=mod.connect_args, poolclass=StaticPool + ) + + with TestClient(mod.app) as client: + hero1_data = {"name": "Deadpond", "secret_name": "Dive Wilson"} + hero2_data = { + "name": "Spider-Boy", + "secret_name": "Pedro Parqueador", + "id": 9000, + } + hero3_data = { + "name": "Rusty-Man", + "secret_name": "Tommy Sharp", + "age": 48, + } + response = client.post("/heroes/", json=hero1_data) + assert response.status_code == 200, response.text + response = client.post("/heroes/", json=hero2_data) + assert response.status_code == 200, response.text + hero2 = response.json() + hero2_id = hero2["id"] + response = client.post("/heroes/", json=hero3_data) + assert response.status_code == 200, response.text + response = client.get(f"/heroes/{hero2_id}") + assert response.status_code == 200, response.text + response = client.get("/heroes/9000") + assert response.status_code == 404, response.text + response = client.get("/heroes/") + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 3 + response = client.patch( + f"/heroes/{hero2_id}", json={"secret_name": "Spider-Youngster"} + ) + assert response.status_code == 200, response.text + response = client.patch("/heroes/9001", json={"name": "Dragon Cube X"}) + assert response.status_code == 404, response.text + + response = client.delete(f"/heroes/{hero2_id}") + assert response.status_code == 200, response.text + response = client.get("/heroes/") + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 2 + + response = client.delete("/heroes/9000") + assert response.status_code == 404, response.text + + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/heroes/": { + "get": { + "summary": "Read Heroes", + "operationId": "read_heroes_heroes__get", + "parameters": [ + { + "required": False, + "schema": { + "title": "Offset", + "type": "integer", + "default": 0, + }, + "name": "offset", + "in": "query", + }, + { + "required": False, + "schema": { + "title": "Limit", + "maximum": 100.0, + "type": "integer", + "default": 100, + }, + "name": "limit", + "in": "query", + }, + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "title": "Response Read Heroes Heroes Get", + "type": "array", + "items": { + "$ref": "#/components/schemas/HeroRead" + }, + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "post": { + "summary": "Create Hero", + "operationId": "create_hero_heroes__post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroCreate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + "/heroes/{hero_id}": { + "get": { + "summary": "Read Hero", + "operationId": "read_hero_heroes__hero_id__get", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "delete": { + "summary": "Delete Hero", + "operationId": "delete_hero_heroes__hero_id__delete", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "patch": { + "summary": "Update Hero", + "operationId": "update_hero_heroes__hero_id__patch", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroUpdate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + }, + "components": { + "schemas": { + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + } + }, + }, + "HeroCreate": { + "title": "HeroCreate", + "required": ["name", "secret_name"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + }, + }, + "HeroRead": { + "title": "HeroRead", + "required": ["name", "secret_name", "id"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + "id": {"title": "Id", "type": "integer"}, + }, + }, + "HeroUpdate": { + "title": "HeroUpdate", + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + } + }, + } diff --git a/tests/test_tutorial/test_fastapi/test_limit_and_offset/test_tutorial001_py310.py b/tests/test_tutorial/test_fastapi/test_limit_and_offset/test_tutorial001_py310.py new file mode 100644 index 00000000..ee0d89ac --- /dev/null +++ b/tests/test_tutorial/test_fastapi/test_limit_and_offset/test_tutorial001_py310.py @@ -0,0 +1,255 @@ +from fastapi.testclient import TestClient +from sqlmodel import create_engine +from sqlmodel.pool import StaticPool + +from ....conftest import needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.fastapi.limit_and_offset import tutorial001_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine( + mod.sqlite_url, connect_args=mod.connect_args, poolclass=StaticPool + ) + + with TestClient(mod.app) as client: + hero1_data = {"name": "Deadpond", "secret_name": "Dive Wilson"} + hero2_data = { + "name": "Spider-Boy", + "secret_name": "Pedro Parqueador", + "id": 9000, + } + hero3_data = { + "name": "Rusty-Man", + "secret_name": "Tommy Sharp", + "age": 48, + } + response = client.post("/heroes/", json=hero1_data) + assert response.status_code == 200, response.text + response = client.post("/heroes/", json=hero2_data) + assert response.status_code == 200, response.text + hero2 = response.json() + hero_id = hero2["id"] + response = client.post("/heroes/", json=hero3_data) + assert response.status_code == 200, response.text + response = client.get(f"/heroes/{hero_id}") + assert response.status_code == 200, response.text + response = client.get("/heroes/9000") + assert response.status_code == 404, response.text + + response = client.get("/heroes/") + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 3 + + response = client.get("/heroes/", params={"limit": 2}) + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 2 + assert data[0]["name"] == hero1_data["name"] + assert data[1]["name"] == hero2_data["name"] + + response = client.get("/heroes/", params={"offset": 1}) + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 2 + assert data[0]["name"] == hero2_data["name"] + assert data[1]["name"] == hero3_data["name"] + + response = client.get("/heroes/", params={"offset": 1, "limit": 1}) + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 1 + assert data[0]["name"] == hero2_data["name"] + + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/heroes/": { + "get": { + "summary": "Read Heroes", + "operationId": "read_heroes_heroes__get", + "parameters": [ + { + "required": False, + "schema": { + "title": "Offset", + "type": "integer", + "default": 0, + }, + "name": "offset", + "in": "query", + }, + { + "required": False, + "schema": { + "title": "Limit", + "maximum": 100.0, + "type": "integer", + "default": 100, + }, + "name": "limit", + "in": "query", + }, + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "title": "Response Read Heroes Heroes Get", + "type": "array", + "items": { + "$ref": "#/components/schemas/HeroRead" + }, + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "post": { + "summary": "Create Hero", + "operationId": "create_hero_heroes__post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroCreate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + "/heroes/{hero_id}": { + "get": { + "summary": "Read Hero", + "operationId": "read_hero_heroes__hero_id__get", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + } + }, + }, + "components": { + "schemas": { + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + } + }, + }, + "HeroCreate": { + "title": "HeroCreate", + "required": ["name", "secret_name"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + }, + }, + "HeroRead": { + "title": "HeroRead", + "required": ["name", "secret_name", "id"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + "id": {"title": "Id", "type": "integer"}, + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + } + }, + } diff --git a/tests/test_tutorial/test_fastapi/test_limit_and_offset/test_tutorial001_py39.py b/tests/test_tutorial/test_fastapi/test_limit_and_offset/test_tutorial001_py39.py new file mode 100644 index 00000000..f4ef44ab --- /dev/null +++ b/tests/test_tutorial/test_fastapi/test_limit_and_offset/test_tutorial001_py39.py @@ -0,0 +1,255 @@ +from fastapi.testclient import TestClient +from sqlmodel import create_engine +from sqlmodel.pool import StaticPool + +from ....conftest import needs_py39 + + +@needs_py39 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.fastapi.limit_and_offset import tutorial001_py39 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine( + mod.sqlite_url, connect_args=mod.connect_args, poolclass=StaticPool + ) + + with TestClient(mod.app) as client: + hero1_data = {"name": "Deadpond", "secret_name": "Dive Wilson"} + hero2_data = { + "name": "Spider-Boy", + "secret_name": "Pedro Parqueador", + "id": 9000, + } + hero3_data = { + "name": "Rusty-Man", + "secret_name": "Tommy Sharp", + "age": 48, + } + response = client.post("/heroes/", json=hero1_data) + assert response.status_code == 200, response.text + response = client.post("/heroes/", json=hero2_data) + assert response.status_code == 200, response.text + hero2 = response.json() + hero_id = hero2["id"] + response = client.post("/heroes/", json=hero3_data) + assert response.status_code == 200, response.text + response = client.get(f"/heroes/{hero_id}") + assert response.status_code == 200, response.text + response = client.get("/heroes/9000") + assert response.status_code == 404, response.text + + response = client.get("/heroes/") + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 3 + + response = client.get("/heroes/", params={"limit": 2}) + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 2 + assert data[0]["name"] == hero1_data["name"] + assert data[1]["name"] == hero2_data["name"] + + response = client.get("/heroes/", params={"offset": 1}) + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 2 + assert data[0]["name"] == hero2_data["name"] + assert data[1]["name"] == hero3_data["name"] + + response = client.get("/heroes/", params={"offset": 1, "limit": 1}) + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 1 + assert data[0]["name"] == hero2_data["name"] + + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/heroes/": { + "get": { + "summary": "Read Heroes", + "operationId": "read_heroes_heroes__get", + "parameters": [ + { + "required": False, + "schema": { + "title": "Offset", + "type": "integer", + "default": 0, + }, + "name": "offset", + "in": "query", + }, + { + "required": False, + "schema": { + "title": "Limit", + "maximum": 100.0, + "type": "integer", + "default": 100, + }, + "name": "limit", + "in": "query", + }, + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "title": "Response Read Heroes Heroes Get", + "type": "array", + "items": { + "$ref": "#/components/schemas/HeroRead" + }, + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "post": { + "summary": "Create Hero", + "operationId": "create_hero_heroes__post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroCreate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + "/heroes/{hero_id}": { + "get": { + "summary": "Read Hero", + "operationId": "read_hero_heroes__hero_id__get", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + } + }, + }, + "components": { + "schemas": { + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + } + }, + }, + "HeroCreate": { + "title": "HeroCreate", + "required": ["name", "secret_name"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + }, + }, + "HeroRead": { + "title": "HeroRead", + "required": ["name", "secret_name", "id"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + "id": {"title": "Id", "type": "integer"}, + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + } + }, + } diff --git a/tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial001_py310.py b/tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial001_py310.py new file mode 100644 index 00000000..080a907e --- /dev/null +++ b/tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial001_py310.py @@ -0,0 +1,203 @@ +from fastapi.testclient import TestClient +from sqlalchemy import inspect +from sqlalchemy.engine.reflection import Inspector +from sqlmodel import create_engine +from sqlmodel.pool import StaticPool + +from ....conftest import needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.fastapi.multiple_models import tutorial001_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine( + mod.sqlite_url, connect_args=mod.connect_args, poolclass=StaticPool + ) + + with TestClient(mod.app) as client: + hero1_data = {"name": "Deadpond", "secret_name": "Dive Wilson"} + hero2_data = { + "name": "Spider-Boy", + "secret_name": "Pedro Parqueador", + "id": 9000, + } + response = client.post("/heroes/", json=hero1_data) + data = response.json() + + assert response.status_code == 200, response.text + assert data["name"] == hero1_data["name"] + assert data["secret_name"] == hero1_data["secret_name"] + assert data["id"] is not None + assert data["age"] is None + + response = client.post("/heroes/", json=hero2_data) + data = response.json() + + assert response.status_code == 200, response.text + assert data["name"] == hero2_data["name"] + assert data["secret_name"] == hero2_data["secret_name"] + assert data["id"] != hero2_data["id"], ( + "Now it's not possible to predefine the ID from the request, " + "it's now set by the database" + ) + assert data["age"] is None + + response = client.get("/heroes/") + data = response.json() + + assert response.status_code == 200, response.text + assert len(data) == 2 + assert data[0]["name"] == hero1_data["name"] + assert data[0]["secret_name"] == hero1_data["secret_name"] + assert data[1]["name"] == hero2_data["name"] + assert data[1]["secret_name"] == hero2_data["secret_name"] + assert data[1]["id"] != hero2_data["id"] + + response = client.get("/openapi.json") + data = response.json() + + assert response.status_code == 200, response.text + + assert data == { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/heroes/": { + "get": { + "summary": "Read Heroes", + "operationId": "read_heroes_heroes__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "title": "Response Read Heroes Heroes Get", + "type": "array", + "items": { + "$ref": "#/components/schemas/HeroRead" + }, + } + } + }, + } + }, + }, + "post": { + "summary": "Create Hero", + "operationId": "create_hero_heroes__post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroCreate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + } + }, + "components": { + "schemas": { + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + } + }, + }, + "HeroCreate": { + "title": "HeroCreate", + "required": ["name", "secret_name"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + }, + }, + "HeroRead": { + "title": "HeroRead", + "required": ["id", "name", "secret_name"], + "type": "object", + "properties": { + "id": {"title": "Id", "type": "integer"}, + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + } + }, + } + + # Test inherited indexes + insp: Inspector = inspect(mod.engine) + indexes = insp.get_indexes(str(mod.Hero.__tablename__)) + expected_indexes = [ + { + "name": "ix_hero_name", + "dialect_options": {}, + "column_names": ["name"], + "unique": 0, + }, + { + "name": "ix_hero_age", + "dialect_options": {}, + "column_names": ["age"], + "unique": 0, + }, + ] + for index in expected_indexes: + assert index in indexes, "This expected index should be in the indexes in DB" + # Now that this index was checked, remove it from the list of indexes + indexes.pop(indexes.index(index)) + assert len(indexes) == 0, "The database should only have the expected indexes" diff --git a/tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial001_py39.py b/tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial001_py39.py new file mode 100644 index 00000000..7c320093 --- /dev/null +++ b/tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial001_py39.py @@ -0,0 +1,203 @@ +from fastapi.testclient import TestClient +from sqlalchemy import inspect +from sqlalchemy.engine.reflection import Inspector +from sqlmodel import create_engine +from sqlmodel.pool import StaticPool + +from ....conftest import needs_py39 + + +@needs_py39 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.fastapi.multiple_models import tutorial001_py39 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine( + mod.sqlite_url, connect_args=mod.connect_args, poolclass=StaticPool + ) + + with TestClient(mod.app) as client: + hero1_data = {"name": "Deadpond", "secret_name": "Dive Wilson"} + hero2_data = { + "name": "Spider-Boy", + "secret_name": "Pedro Parqueador", + "id": 9000, + } + response = client.post("/heroes/", json=hero1_data) + data = response.json() + + assert response.status_code == 200, response.text + assert data["name"] == hero1_data["name"] + assert data["secret_name"] == hero1_data["secret_name"] + assert data["id"] is not None + assert data["age"] is None + + response = client.post("/heroes/", json=hero2_data) + data = response.json() + + assert response.status_code == 200, response.text + assert data["name"] == hero2_data["name"] + assert data["secret_name"] == hero2_data["secret_name"] + assert data["id"] != hero2_data["id"], ( + "Now it's not possible to predefine the ID from the request, " + "it's now set by the database" + ) + assert data["age"] is None + + response = client.get("/heroes/") + data = response.json() + + assert response.status_code == 200, response.text + assert len(data) == 2 + assert data[0]["name"] == hero1_data["name"] + assert data[0]["secret_name"] == hero1_data["secret_name"] + assert data[1]["name"] == hero2_data["name"] + assert data[1]["secret_name"] == hero2_data["secret_name"] + assert data[1]["id"] != hero2_data["id"] + + response = client.get("/openapi.json") + data = response.json() + + assert response.status_code == 200, response.text + + assert data == { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/heroes/": { + "get": { + "summary": "Read Heroes", + "operationId": "read_heroes_heroes__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "title": "Response Read Heroes Heroes Get", + "type": "array", + "items": { + "$ref": "#/components/schemas/HeroRead" + }, + } + } + }, + } + }, + }, + "post": { + "summary": "Create Hero", + "operationId": "create_hero_heroes__post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroCreate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + } + }, + "components": { + "schemas": { + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + } + }, + }, + "HeroCreate": { + "title": "HeroCreate", + "required": ["name", "secret_name"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + }, + }, + "HeroRead": { + "title": "HeroRead", + "required": ["id", "name", "secret_name"], + "type": "object", + "properties": { + "id": {"title": "Id", "type": "integer"}, + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + } + }, + } + + # Test inherited indexes + insp: Inspector = inspect(mod.engine) + indexes = insp.get_indexes(str(mod.Hero.__tablename__)) + expected_indexes = [ + { + "name": "ix_hero_name", + "dialect_options": {}, + "column_names": ["name"], + "unique": 0, + }, + { + "name": "ix_hero_age", + "dialect_options": {}, + "column_names": ["age"], + "unique": 0, + }, + ] + for index in expected_indexes: + assert index in indexes, "This expected index should be in the indexes in DB" + # Now that this index was checked, remove it from the list of indexes + indexes.pop(indexes.index(index)) + assert len(indexes) == 0, "The database should only have the expected indexes" diff --git a/tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial002_py310.py b/tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial002_py310.py new file mode 100644 index 00000000..20195c6f --- /dev/null +++ b/tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial002_py310.py @@ -0,0 +1,203 @@ +from fastapi.testclient import TestClient +from sqlalchemy import inspect +from sqlalchemy.engine.reflection import Inspector +from sqlmodel import create_engine +from sqlmodel.pool import StaticPool + +from ....conftest import needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.fastapi.multiple_models import tutorial002_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine( + mod.sqlite_url, connect_args=mod.connect_args, poolclass=StaticPool + ) + + with TestClient(mod.app) as client: + hero1_data = {"name": "Deadpond", "secret_name": "Dive Wilson"} + hero2_data = { + "name": "Spider-Boy", + "secret_name": "Pedro Parqueador", + "id": 9000, + } + response = client.post("/heroes/", json=hero1_data) + data = response.json() + + assert response.status_code == 200, response.text + assert data["name"] == hero1_data["name"] + assert data["secret_name"] == hero1_data["secret_name"] + assert data["id"] is not None + assert data["age"] is None + + response = client.post("/heroes/", json=hero2_data) + data = response.json() + + assert response.status_code == 200, response.text + assert data["name"] == hero2_data["name"] + assert data["secret_name"] == hero2_data["secret_name"] + assert data["id"] != hero2_data["id"], ( + "Now it's not possible to predefine the ID from the request, " + "it's now set by the database" + ) + assert data["age"] is None + + response = client.get("/heroes/") + data = response.json() + + assert response.status_code == 200, response.text + assert len(data) == 2 + assert data[0]["name"] == hero1_data["name"] + assert data[0]["secret_name"] == hero1_data["secret_name"] + assert data[1]["name"] == hero2_data["name"] + assert data[1]["secret_name"] == hero2_data["secret_name"] + assert data[1]["id"] != hero2_data["id"] + + response = client.get("/openapi.json") + data = response.json() + + assert response.status_code == 200, response.text + + assert data == { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/heroes/": { + "get": { + "summary": "Read Heroes", + "operationId": "read_heroes_heroes__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "title": "Response Read Heroes Heroes Get", + "type": "array", + "items": { + "$ref": "#/components/schemas/HeroRead" + }, + } + } + }, + } + }, + }, + "post": { + "summary": "Create Hero", + "operationId": "create_hero_heroes__post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroCreate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + } + }, + "components": { + "schemas": { + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + } + }, + }, + "HeroCreate": { + "title": "HeroCreate", + "required": ["name", "secret_name"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + }, + }, + "HeroRead": { + "title": "HeroRead", + "required": ["name", "secret_name", "id"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + "id": {"title": "Id", "type": "integer"}, + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + } + }, + } + + # Test inherited indexes + insp: Inspector = inspect(mod.engine) + indexes = insp.get_indexes(str(mod.Hero.__tablename__)) + expected_indexes = [ + { + "name": "ix_hero_age", + "dialect_options": {}, + "column_names": ["age"], + "unique": 0, + }, + { + "name": "ix_hero_name", + "dialect_options": {}, + "column_names": ["name"], + "unique": 0, + }, + ] + for index in expected_indexes: + assert index in indexes, "This expected index should be in the indexes in DB" + # Now that this index was checked, remove it from the list of indexes + indexes.pop(indexes.index(index)) + assert len(indexes) == 0, "The database should only have the expected indexes" diff --git a/tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial002_py39.py b/tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial002_py39.py new file mode 100644 index 00000000..45b061b4 --- /dev/null +++ b/tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial002_py39.py @@ -0,0 +1,203 @@ +from fastapi.testclient import TestClient +from sqlalchemy import inspect +from sqlalchemy.engine.reflection import Inspector +from sqlmodel import create_engine +from sqlmodel.pool import StaticPool + +from ....conftest import needs_py39 + + +@needs_py39 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.fastapi.multiple_models import tutorial002_py39 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine( + mod.sqlite_url, connect_args=mod.connect_args, poolclass=StaticPool + ) + + with TestClient(mod.app) as client: + hero1_data = {"name": "Deadpond", "secret_name": "Dive Wilson"} + hero2_data = { + "name": "Spider-Boy", + "secret_name": "Pedro Parqueador", + "id": 9000, + } + response = client.post("/heroes/", json=hero1_data) + data = response.json() + + assert response.status_code == 200, response.text + assert data["name"] == hero1_data["name"] + assert data["secret_name"] == hero1_data["secret_name"] + assert data["id"] is not None + assert data["age"] is None + + response = client.post("/heroes/", json=hero2_data) + data = response.json() + + assert response.status_code == 200, response.text + assert data["name"] == hero2_data["name"] + assert data["secret_name"] == hero2_data["secret_name"] + assert data["id"] != hero2_data["id"], ( + "Now it's not possible to predefine the ID from the request, " + "it's now set by the database" + ) + assert data["age"] is None + + response = client.get("/heroes/") + data = response.json() + + assert response.status_code == 200, response.text + assert len(data) == 2 + assert data[0]["name"] == hero1_data["name"] + assert data[0]["secret_name"] == hero1_data["secret_name"] + assert data[1]["name"] == hero2_data["name"] + assert data[1]["secret_name"] == hero2_data["secret_name"] + assert data[1]["id"] != hero2_data["id"] + + response = client.get("/openapi.json") + data = response.json() + + assert response.status_code == 200, response.text + + assert data == { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/heroes/": { + "get": { + "summary": "Read Heroes", + "operationId": "read_heroes_heroes__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "title": "Response Read Heroes Heroes Get", + "type": "array", + "items": { + "$ref": "#/components/schemas/HeroRead" + }, + } + } + }, + } + }, + }, + "post": { + "summary": "Create Hero", + "operationId": "create_hero_heroes__post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroCreate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + } + }, + "components": { + "schemas": { + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + } + }, + }, + "HeroCreate": { + "title": "HeroCreate", + "required": ["name", "secret_name"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + }, + }, + "HeroRead": { + "title": "HeroRead", + "required": ["name", "secret_name", "id"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + "id": {"title": "Id", "type": "integer"}, + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + } + }, + } + + # Test inherited indexes + insp: Inspector = inspect(mod.engine) + indexes = insp.get_indexes(str(mod.Hero.__tablename__)) + expected_indexes = [ + { + "name": "ix_hero_age", + "dialect_options": {}, + "column_names": ["age"], + "unique": 0, + }, + { + "name": "ix_hero_name", + "dialect_options": {}, + "column_names": ["name"], + "unique": 0, + }, + ] + for index in expected_indexes: + assert index in indexes, "This expected index should be in the indexes in DB" + # Now that this index was checked, remove it from the list of indexes + indexes.pop(indexes.index(index)) + assert len(indexes) == 0, "The database should only have the expected indexes" diff --git a/tests/test_tutorial/test_fastapi/test_read_one/test_tutorial001_py310.py b/tests/test_tutorial/test_fastapi/test_read_one/test_tutorial001_py310.py new file mode 100644 index 00000000..2e0a97e7 --- /dev/null +++ b/tests/test_tutorial/test_fastapi/test_read_one/test_tutorial001_py310.py @@ -0,0 +1,201 @@ +from fastapi.testclient import TestClient +from sqlmodel import create_engine +from sqlmodel.pool import StaticPool + +from ....conftest import needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.fastapi.read_one import tutorial001_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine( + mod.sqlite_url, connect_args=mod.connect_args, poolclass=StaticPool + ) + + with TestClient(mod.app) as client: + hero1_data = {"name": "Deadpond", "secret_name": "Dive Wilson"} + hero2_data = { + "name": "Spider-Boy", + "secret_name": "Pedro Parqueador", + "id": 9000, + } + response = client.post("/heroes/", json=hero1_data) + assert response.status_code == 200, response.text + response = client.post("/heroes/", json=hero2_data) + assert response.status_code == 200, response.text + hero2 = response.json() + response = client.get("/heroes/") + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 2 + + hero_id = hero2["id"] + response = client.get(f"/heroes/{hero_id}") + assert response.status_code == 200, response.text + data = response.json() + assert data == hero2 + + response = client.get("/heroes/9000") + assert response.status_code == 404, response.text + + response = client.get("/openapi.json") + data = response.json() + + assert response.status_code == 200, response.text + + assert data == { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/heroes/": { + "get": { + "summary": "Read Heroes", + "operationId": "read_heroes_heroes__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "title": "Response Read Heroes Heroes Get", + "type": "array", + "items": { + "$ref": "#/components/schemas/HeroRead" + }, + } + } + }, + } + }, + }, + "post": { + "summary": "Create Hero", + "operationId": "create_hero_heroes__post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroCreate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + "/heroes/{hero_id}": { + "get": { + "summary": "Read Hero", + "operationId": "read_hero_heroes__hero_id__get", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + } + }, + }, + "components": { + "schemas": { + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + } + }, + }, + "HeroCreate": { + "title": "HeroCreate", + "required": ["name", "secret_name"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + }, + }, + "HeroRead": { + "title": "HeroRead", + "required": ["name", "secret_name", "id"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + "id": {"title": "Id", "type": "integer"}, + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + } + }, + } diff --git a/tests/test_tutorial/test_fastapi/test_read_one/test_tutorial001_py39.py b/tests/test_tutorial/test_fastapi/test_read_one/test_tutorial001_py39.py new file mode 100644 index 00000000..a663ecca --- /dev/null +++ b/tests/test_tutorial/test_fastapi/test_read_one/test_tutorial001_py39.py @@ -0,0 +1,201 @@ +from fastapi.testclient import TestClient +from sqlmodel import create_engine +from sqlmodel.pool import StaticPool + +from ....conftest import needs_py39 + + +@needs_py39 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.fastapi.read_one import tutorial001_py39 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine( + mod.sqlite_url, connect_args=mod.connect_args, poolclass=StaticPool + ) + + with TestClient(mod.app) as client: + hero1_data = {"name": "Deadpond", "secret_name": "Dive Wilson"} + hero2_data = { + "name": "Spider-Boy", + "secret_name": "Pedro Parqueador", + "id": 9000, + } + response = client.post("/heroes/", json=hero1_data) + assert response.status_code == 200, response.text + response = client.post("/heroes/", json=hero2_data) + assert response.status_code == 200, response.text + hero2 = response.json() + response = client.get("/heroes/") + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 2 + + hero_id = hero2["id"] + response = client.get(f"/heroes/{hero_id}") + assert response.status_code == 200, response.text + data = response.json() + assert data == hero2 + + response = client.get("/heroes/9000") + assert response.status_code == 404, response.text + + response = client.get("/openapi.json") + data = response.json() + + assert response.status_code == 200, response.text + + assert data == { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/heroes/": { + "get": { + "summary": "Read Heroes", + "operationId": "read_heroes_heroes__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "title": "Response Read Heroes Heroes Get", + "type": "array", + "items": { + "$ref": "#/components/schemas/HeroRead" + }, + } + } + }, + } + }, + }, + "post": { + "summary": "Create Hero", + "operationId": "create_hero_heroes__post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroCreate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + "/heroes/{hero_id}": { + "get": { + "summary": "Read Hero", + "operationId": "read_hero_heroes__hero_id__get", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + } + }, + }, + "components": { + "schemas": { + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + } + }, + }, + "HeroCreate": { + "title": "HeroCreate", + "required": ["name", "secret_name"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + }, + }, + "HeroRead": { + "title": "HeroRead", + "required": ["name", "secret_name", "id"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + "id": {"title": "Id", "type": "integer"}, + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + } + }, + } diff --git a/tests/test_tutorial/test_fastapi/test_relationships/test_tutorial001_py310.py b/tests/test_tutorial/test_fastapi/test_relationships/test_tutorial001_py310.py new file mode 100644 index 00000000..dae7db33 --- /dev/null +++ b/tests/test_tutorial/test_fastapi/test_relationships/test_tutorial001_py310.py @@ -0,0 +1,638 @@ +from fastapi.testclient import TestClient +from sqlmodel import create_engine +from sqlmodel.pool import StaticPool + +from ....conftest import needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.fastapi.relationships import tutorial001_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine( + mod.sqlite_url, connect_args=mod.connect_args, poolclass=StaticPool + ) + + with TestClient(mod.app) as client: + team_preventers = {"name": "Preventers", "headquarters": "Sharp Tower"} + team_z_force = {"name": "Z-Force", "headquarters": "Sister Margaret’s Bar"} + response = client.post("/teams/", json=team_preventers) + assert response.status_code == 200, response.text + team_preventers_data = response.json() + team_preventers_id = team_preventers_data["id"] + response = client.post("/teams/", json=team_z_force) + assert response.status_code == 200, response.text + team_z_force_data = response.json() + team_z_force_id = team_z_force_data["id"] + response = client.get("/teams/") + data = response.json() + assert len(data) == 2 + response = client.get("/teams/9000") + assert response.status_code == 404, response.text + response = client.patch( + f"/teams/{team_preventers_id}", json={"headquarters": "Preventers Tower"} + ) + data = response.json() + assert response.status_code == 200, response.text + assert data["name"] == team_preventers["name"] + assert data["headquarters"] == "Preventers Tower" + response = client.patch("/teams/9000", json={"name": "Freedom League"}) + assert response.status_code == 404, response.text + + hero1_data = { + "name": "Deadpond", + "secret_name": "Dive Wilson", + "team_id": team_z_force_id, + } + hero2_data = { + "name": "Spider-Boy", + "secret_name": "Pedro Parqueador", + "id": 9000, + } + hero3_data = { + "name": "Rusty-Man", + "secret_name": "Tommy Sharp", + "age": 48, + "team_id": team_preventers_id, + } + response = client.post("/heroes/", json=hero1_data) + assert response.status_code == 200, response.text + hero1 = response.json() + hero1_id = hero1["id"] + response = client.post("/heroes/", json=hero2_data) + assert response.status_code == 200, response.text + hero2 = response.json() + hero2_id = hero2["id"] + response = client.post("/heroes/", json=hero3_data) + assert response.status_code == 200, response.text + response = client.get("/heroes/9000") + assert response.status_code == 404, response.text + response = client.get("/heroes/") + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 3 + response = client.get(f"/heroes/{hero1_id}") + assert response.status_code == 200, response.text + data = response.json() + assert data["name"] == hero1_data["name"] + assert data["team"]["name"] == team_z_force["name"] + response = client.patch( + f"/heroes/{hero2_id}", json={"secret_name": "Spider-Youngster"} + ) + assert response.status_code == 200, response.text + response = client.patch("/heroes/9001", json={"name": "Dragon Cube X"}) + assert response.status_code == 404, response.text + response = client.delete(f"/heroes/{hero2_id}") + assert response.status_code == 200, response.text + response = client.get("/heroes/") + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 2 + response = client.delete("/heroes/9000") + assert response.status_code == 404, response.text + + response = client.get(f"/teams/{team_preventers_id}") + data = response.json() + assert response.status_code == 200, response.text + assert data["name"] == team_preventers_data["name"] + assert data["heroes"][0]["name"] == hero3_data["name"] + + response = client.delete(f"/teams/{team_preventers_id}") + assert response.status_code == 200, response.text + response = client.delete("/teams/9000") + assert response.status_code == 404, response.text + response = client.get("/teams/") + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 1 + + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/heroes/": { + "get": { + "summary": "Read Heroes", + "operationId": "read_heroes_heroes__get", + "parameters": [ + { + "required": False, + "schema": { + "title": "Offset", + "type": "integer", + "default": 0, + }, + "name": "offset", + "in": "query", + }, + { + "required": False, + "schema": { + "title": "Limit", + "maximum": 100.0, + "type": "integer", + "default": 100, + }, + "name": "limit", + "in": "query", + }, + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "title": "Response Read Heroes Heroes Get", + "type": "array", + "items": { + "$ref": "#/components/schemas/HeroRead" + }, + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "post": { + "summary": "Create Hero", + "operationId": "create_hero_heroes__post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroCreate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + "/heroes/{hero_id}": { + "get": { + "summary": "Read Hero", + "operationId": "read_hero_heroes__hero_id__get", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroReadWithTeam" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "delete": { + "summary": "Delete Hero", + "operationId": "delete_hero_heroes__hero_id__delete", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "patch": { + "summary": "Update Hero", + "operationId": "update_hero_heroes__hero_id__patch", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroUpdate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + "/teams/": { + "get": { + "summary": "Read Teams", + "operationId": "read_teams_teams__get", + "parameters": [ + { + "required": False, + "schema": { + "title": "Offset", + "type": "integer", + "default": 0, + }, + "name": "offset", + "in": "query", + }, + { + "required": False, + "schema": { + "title": "Limit", + "maximum": 100.0, + "type": "integer", + "default": 100, + }, + "name": "limit", + "in": "query", + }, + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "title": "Response Read Teams Teams Get", + "type": "array", + "items": { + "$ref": "#/components/schemas/TeamRead" + }, + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "post": { + "summary": "Create Team", + "operationId": "create_team_teams__post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TeamCreate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TeamRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + "/teams/{team_id}": { + "get": { + "summary": "Read Team", + "operationId": "read_team_teams__team_id__get", + "parameters": [ + { + "required": True, + "schema": {"title": "Team Id", "type": "integer"}, + "name": "team_id", + "in": "path", + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TeamReadWithHeroes" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "delete": { + "summary": "Delete Team", + "operationId": "delete_team_teams__team_id__delete", + "parameters": [ + { + "required": True, + "schema": {"title": "Team Id", "type": "integer"}, + "name": "team_id", + "in": "path", + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "patch": { + "summary": "Update Team", + "operationId": "update_team_teams__team_id__patch", + "parameters": [ + { + "required": True, + "schema": {"title": "Team Id", "type": "integer"}, + "name": "team_id", + "in": "path", + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TeamUpdate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TeamRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + }, + "components": { + "schemas": { + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + } + }, + }, + "HeroCreate": { + "title": "HeroCreate", + "required": ["name", "secret_name"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + "team_id": {"title": "Team Id", "type": "integer"}, + }, + }, + "HeroRead": { + "title": "HeroRead", + "required": ["name", "secret_name", "id"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + "team_id": {"title": "Team Id", "type": "integer"}, + "id": {"title": "Id", "type": "integer"}, + }, + }, + "HeroReadWithTeam": { + "title": "HeroReadWithTeam", + "required": ["name", "secret_name", "id"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + "team_id": {"title": "Team Id", "type": "integer"}, + "id": {"title": "Id", "type": "integer"}, + "team": {"$ref": "#/components/schemas/TeamRead"}, + }, + }, + "HeroUpdate": { + "title": "HeroUpdate", + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + "team_id": {"title": "Team Id", "type": "integer"}, + }, + }, + "TeamCreate": { + "title": "TeamCreate", + "required": ["name", "headquarters"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "headquarters": {"title": "Headquarters", "type": "string"}, + }, + }, + "TeamRead": { + "title": "TeamRead", + "required": ["name", "headquarters", "id"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "headquarters": {"title": "Headquarters", "type": "string"}, + "id": {"title": "Id", "type": "integer"}, + }, + }, + "TeamReadWithHeroes": { + "title": "TeamReadWithHeroes", + "required": ["name", "headquarters", "id"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "headquarters": {"title": "Headquarters", "type": "string"}, + "id": {"title": "Id", "type": "integer"}, + "heroes": { + "title": "Heroes", + "type": "array", + "items": {"$ref": "#/components/schemas/HeroRead"}, + "default": [], + }, + }, + }, + "TeamUpdate": { + "title": "TeamUpdate", + "type": "object", + "properties": { + "id": {"title": "Id", "type": "integer"}, + "name": {"title": "Name", "type": "string"}, + "headquarters": {"title": "Headquarters", "type": "string"}, + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + } + }, + } diff --git a/tests/test_tutorial/test_fastapi/test_relationships/test_tutorial001_py39.py b/tests/test_tutorial/test_fastapi/test_relationships/test_tutorial001_py39.py new file mode 100644 index 00000000..72dee334 --- /dev/null +++ b/tests/test_tutorial/test_fastapi/test_relationships/test_tutorial001_py39.py @@ -0,0 +1,638 @@ +from fastapi.testclient import TestClient +from sqlmodel import create_engine +from sqlmodel.pool import StaticPool + +from ....conftest import needs_py39 + + +@needs_py39 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.fastapi.relationships import tutorial001_py39 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine( + mod.sqlite_url, connect_args=mod.connect_args, poolclass=StaticPool + ) + + with TestClient(mod.app) as client: + team_preventers = {"name": "Preventers", "headquarters": "Sharp Tower"} + team_z_force = {"name": "Z-Force", "headquarters": "Sister Margaret’s Bar"} + response = client.post("/teams/", json=team_preventers) + assert response.status_code == 200, response.text + team_preventers_data = response.json() + team_preventers_id = team_preventers_data["id"] + response = client.post("/teams/", json=team_z_force) + assert response.status_code == 200, response.text + team_z_force_data = response.json() + team_z_force_id = team_z_force_data["id"] + response = client.get("/teams/") + data = response.json() + assert len(data) == 2 + response = client.get("/teams/9000") + assert response.status_code == 404, response.text + response = client.patch( + f"/teams/{team_preventers_id}", json={"headquarters": "Preventers Tower"} + ) + data = response.json() + assert response.status_code == 200, response.text + assert data["name"] == team_preventers["name"] + assert data["headquarters"] == "Preventers Tower" + response = client.patch("/teams/9000", json={"name": "Freedom League"}) + assert response.status_code == 404, response.text + + hero1_data = { + "name": "Deadpond", + "secret_name": "Dive Wilson", + "team_id": team_z_force_id, + } + hero2_data = { + "name": "Spider-Boy", + "secret_name": "Pedro Parqueador", + "id": 9000, + } + hero3_data = { + "name": "Rusty-Man", + "secret_name": "Tommy Sharp", + "age": 48, + "team_id": team_preventers_id, + } + response = client.post("/heroes/", json=hero1_data) + assert response.status_code == 200, response.text + hero1 = response.json() + hero1_id = hero1["id"] + response = client.post("/heroes/", json=hero2_data) + assert response.status_code == 200, response.text + hero2 = response.json() + hero2_id = hero2["id"] + response = client.post("/heroes/", json=hero3_data) + assert response.status_code == 200, response.text + response = client.get("/heroes/9000") + assert response.status_code == 404, response.text + response = client.get("/heroes/") + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 3 + response = client.get(f"/heroes/{hero1_id}") + assert response.status_code == 200, response.text + data = response.json() + assert data["name"] == hero1_data["name"] + assert data["team"]["name"] == team_z_force["name"] + response = client.patch( + f"/heroes/{hero2_id}", json={"secret_name": "Spider-Youngster"} + ) + assert response.status_code == 200, response.text + response = client.patch("/heroes/9001", json={"name": "Dragon Cube X"}) + assert response.status_code == 404, response.text + response = client.delete(f"/heroes/{hero2_id}") + assert response.status_code == 200, response.text + response = client.get("/heroes/") + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 2 + response = client.delete("/heroes/9000") + assert response.status_code == 404, response.text + + response = client.get(f"/teams/{team_preventers_id}") + data = response.json() + assert response.status_code == 200, response.text + assert data["name"] == team_preventers_data["name"] + assert data["heroes"][0]["name"] == hero3_data["name"] + + response = client.delete(f"/teams/{team_preventers_id}") + assert response.status_code == 200, response.text + response = client.delete("/teams/9000") + assert response.status_code == 404, response.text + response = client.get("/teams/") + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 1 + + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/heroes/": { + "get": { + "summary": "Read Heroes", + "operationId": "read_heroes_heroes__get", + "parameters": [ + { + "required": False, + "schema": { + "title": "Offset", + "type": "integer", + "default": 0, + }, + "name": "offset", + "in": "query", + }, + { + "required": False, + "schema": { + "title": "Limit", + "maximum": 100.0, + "type": "integer", + "default": 100, + }, + "name": "limit", + "in": "query", + }, + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "title": "Response Read Heroes Heroes Get", + "type": "array", + "items": { + "$ref": "#/components/schemas/HeroRead" + }, + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "post": { + "summary": "Create Hero", + "operationId": "create_hero_heroes__post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroCreate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + "/heroes/{hero_id}": { + "get": { + "summary": "Read Hero", + "operationId": "read_hero_heroes__hero_id__get", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroReadWithTeam" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "delete": { + "summary": "Delete Hero", + "operationId": "delete_hero_heroes__hero_id__delete", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "patch": { + "summary": "Update Hero", + "operationId": "update_hero_heroes__hero_id__patch", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroUpdate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + "/teams/": { + "get": { + "summary": "Read Teams", + "operationId": "read_teams_teams__get", + "parameters": [ + { + "required": False, + "schema": { + "title": "Offset", + "type": "integer", + "default": 0, + }, + "name": "offset", + "in": "query", + }, + { + "required": False, + "schema": { + "title": "Limit", + "maximum": 100.0, + "type": "integer", + "default": 100, + }, + "name": "limit", + "in": "query", + }, + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "title": "Response Read Teams Teams Get", + "type": "array", + "items": { + "$ref": "#/components/schemas/TeamRead" + }, + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "post": { + "summary": "Create Team", + "operationId": "create_team_teams__post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TeamCreate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TeamRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + "/teams/{team_id}": { + "get": { + "summary": "Read Team", + "operationId": "read_team_teams__team_id__get", + "parameters": [ + { + "required": True, + "schema": {"title": "Team Id", "type": "integer"}, + "name": "team_id", + "in": "path", + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TeamReadWithHeroes" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "delete": { + "summary": "Delete Team", + "operationId": "delete_team_teams__team_id__delete", + "parameters": [ + { + "required": True, + "schema": {"title": "Team Id", "type": "integer"}, + "name": "team_id", + "in": "path", + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "patch": { + "summary": "Update Team", + "operationId": "update_team_teams__team_id__patch", + "parameters": [ + { + "required": True, + "schema": {"title": "Team Id", "type": "integer"}, + "name": "team_id", + "in": "path", + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TeamUpdate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TeamRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + }, + "components": { + "schemas": { + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + } + }, + }, + "HeroCreate": { + "title": "HeroCreate", + "required": ["name", "secret_name"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + "team_id": {"title": "Team Id", "type": "integer"}, + }, + }, + "HeroRead": { + "title": "HeroRead", + "required": ["name", "secret_name", "id"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + "team_id": {"title": "Team Id", "type": "integer"}, + "id": {"title": "Id", "type": "integer"}, + }, + }, + "HeroReadWithTeam": { + "title": "HeroReadWithTeam", + "required": ["name", "secret_name", "id"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + "team_id": {"title": "Team Id", "type": "integer"}, + "id": {"title": "Id", "type": "integer"}, + "team": {"$ref": "#/components/schemas/TeamRead"}, + }, + }, + "HeroUpdate": { + "title": "HeroUpdate", + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + "team_id": {"title": "Team Id", "type": "integer"}, + }, + }, + "TeamCreate": { + "title": "TeamCreate", + "required": ["name", "headquarters"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "headquarters": {"title": "Headquarters", "type": "string"}, + }, + }, + "TeamRead": { + "title": "TeamRead", + "required": ["name", "headquarters", "id"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "headquarters": {"title": "Headquarters", "type": "string"}, + "id": {"title": "Id", "type": "integer"}, + }, + }, + "TeamReadWithHeroes": { + "title": "TeamReadWithHeroes", + "required": ["name", "headquarters", "id"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "headquarters": {"title": "Headquarters", "type": "string"}, + "id": {"title": "Id", "type": "integer"}, + "heroes": { + "title": "Heroes", + "type": "array", + "items": {"$ref": "#/components/schemas/HeroRead"}, + "default": [], + }, + }, + }, + "TeamUpdate": { + "title": "TeamUpdate", + "type": "object", + "properties": { + "id": {"title": "Id", "type": "integer"}, + "name": {"title": "Name", "type": "string"}, + "headquarters": {"title": "Headquarters", "type": "string"}, + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + } + }, + } diff --git a/tests/test_tutorial/test_fastapi/test_response_model/test_tutorial001_py310.py b/tests/test_tutorial/test_fastapi/test_response_model/test_tutorial001_py310.py new file mode 100644 index 00000000..4acb0068 --- /dev/null +++ b/tests/test_tutorial/test_fastapi/test_response_model/test_tutorial001_py310.py @@ -0,0 +1,144 @@ +from fastapi.testclient import TestClient +from sqlmodel import create_engine +from sqlmodel.pool import StaticPool + +from ....conftest import needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.fastapi.response_model import tutorial001_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine( + mod.sqlite_url, connect_args=mod.connect_args, poolclass=StaticPool + ) + + with TestClient(mod.app) as client: + hero_data = {"name": "Deadpond", "secret_name": "Dive Wilson"} + response = client.post("/heroes/", json=hero_data) + data = response.json() + + assert response.status_code == 200, response.text + assert data["name"] == hero_data["name"] + assert data["secret_name"] == hero_data["secret_name"] + assert data["id"] is not None + assert data["age"] is None + + response = client.get("/heroes/") + data = response.json() + + assert response.status_code == 200, response.text + assert len(data) == 1 + assert data[0]["name"] == hero_data["name"] + assert data[0]["secret_name"] == hero_data["secret_name"] + + response = client.get("/openapi.json") + data = response.json() + + assert response.status_code == 200, response.text + + assert data == { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/heroes/": { + "get": { + "summary": "Read Heroes", + "operationId": "read_heroes_heroes__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "title": "Response Read Heroes Heroes Get", + "type": "array", + "items": { + "$ref": "#/components/schemas/Hero" + }, + } + } + }, + } + }, + }, + "post": { + "summary": "Create Hero", + "operationId": "create_hero_heroes__post", + "requestBody": { + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/Hero"} + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/Hero"} + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + } + }, + "components": { + "schemas": { + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + } + }, + }, + "Hero": { + "title": "Hero", + "required": ["name", "secret_name"], + "type": "object", + "properties": { + "id": {"title": "Id", "type": "integer"}, + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + } + }, + } diff --git a/tests/test_tutorial/test_fastapi/test_response_model/test_tutorial001_py39.py b/tests/test_tutorial/test_fastapi/test_response_model/test_tutorial001_py39.py new file mode 100644 index 00000000..20f3f523 --- /dev/null +++ b/tests/test_tutorial/test_fastapi/test_response_model/test_tutorial001_py39.py @@ -0,0 +1,144 @@ +from fastapi.testclient import TestClient +from sqlmodel import create_engine +from sqlmodel.pool import StaticPool + +from ....conftest import needs_py39 + + +@needs_py39 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.fastapi.response_model import tutorial001_py39 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine( + mod.sqlite_url, connect_args=mod.connect_args, poolclass=StaticPool + ) + + with TestClient(mod.app) as client: + hero_data = {"name": "Deadpond", "secret_name": "Dive Wilson"} + response = client.post("/heroes/", json=hero_data) + data = response.json() + + assert response.status_code == 200, response.text + assert data["name"] == hero_data["name"] + assert data["secret_name"] == hero_data["secret_name"] + assert data["id"] is not None + assert data["age"] is None + + response = client.get("/heroes/") + data = response.json() + + assert response.status_code == 200, response.text + assert len(data) == 1 + assert data[0]["name"] == hero_data["name"] + assert data[0]["secret_name"] == hero_data["secret_name"] + + response = client.get("/openapi.json") + data = response.json() + + assert response.status_code == 200, response.text + + assert data == { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/heroes/": { + "get": { + "summary": "Read Heroes", + "operationId": "read_heroes_heroes__get", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "title": "Response Read Heroes Heroes Get", + "type": "array", + "items": { + "$ref": "#/components/schemas/Hero" + }, + } + } + }, + } + }, + }, + "post": { + "summary": "Create Hero", + "operationId": "create_hero_heroes__post", + "requestBody": { + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/Hero"} + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/Hero"} + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + } + }, + "components": { + "schemas": { + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + } + }, + }, + "Hero": { + "title": "Hero", + "required": ["name", "secret_name"], + "type": "object", + "properties": { + "id": {"title": "Id", "type": "integer"}, + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + } + }, + } diff --git a/tests/test_tutorial/test_fastapi/test_session_with_dependency/test_tutorial001_py310.py b/tests/test_tutorial/test_fastapi/test_session_with_dependency/test_tutorial001_py310.py new file mode 100644 index 00000000..f0c5416b --- /dev/null +++ b/tests/test_tutorial/test_fastapi/test_session_with_dependency/test_tutorial001_py310.py @@ -0,0 +1,333 @@ +from fastapi.testclient import TestClient +from sqlmodel import create_engine +from sqlmodel.pool import StaticPool + +from ....conftest import needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.fastapi.session_with_dependency import ( + tutorial001_py310 as mod, + ) + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine( + mod.sqlite_url, connect_args=mod.connect_args, poolclass=StaticPool + ) + + with TestClient(mod.app) as client: + hero1_data = {"name": "Deadpond", "secret_name": "Dive Wilson"} + hero2_data = { + "name": "Spider-Boy", + "secret_name": "Pedro Parqueador", + "id": 9000, + } + hero3_data = { + "name": "Rusty-Man", + "secret_name": "Tommy Sharp", + "age": 48, + } + response = client.post("/heroes/", json=hero1_data) + assert response.status_code == 200, response.text + response = client.post("/heroes/", json=hero2_data) + assert response.status_code == 200, response.text + hero2 = response.json() + hero2_id = hero2["id"] + response = client.post("/heroes/", json=hero3_data) + assert response.status_code == 200, response.text + response = client.get(f"/heroes/{hero2_id}") + assert response.status_code == 200, response.text + response = client.get("/heroes/9000") + assert response.status_code == 404, response.text + response = client.get("/heroes/") + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 3 + response = client.patch( + f"/heroes/{hero2_id}", json={"secret_name": "Spider-Youngster"} + ) + assert response.status_code == 200, response.text + response = client.patch("/heroes/9001", json={"name": "Dragon Cube X"}) + assert response.status_code == 404, response.text + + response = client.delete(f"/heroes/{hero2_id}") + assert response.status_code == 200, response.text + response = client.get("/heroes/") + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 2 + + response = client.delete("/heroes/9000") + assert response.status_code == 404, response.text + + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/heroes/": { + "get": { + "summary": "Read Heroes", + "operationId": "read_heroes_heroes__get", + "parameters": [ + { + "required": False, + "schema": { + "title": "Offset", + "type": "integer", + "default": 0, + }, + "name": "offset", + "in": "query", + }, + { + "required": False, + "schema": { + "title": "Limit", + "maximum": 100.0, + "type": "integer", + "default": 100, + }, + "name": "limit", + "in": "query", + }, + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "title": "Response Read Heroes Heroes Get", + "type": "array", + "items": { + "$ref": "#/components/schemas/HeroRead" + }, + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "post": { + "summary": "Create Hero", + "operationId": "create_hero_heroes__post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroCreate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + "/heroes/{hero_id}": { + "get": { + "summary": "Read Hero", + "operationId": "read_hero_heroes__hero_id__get", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "delete": { + "summary": "Delete Hero", + "operationId": "delete_hero_heroes__hero_id__delete", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "patch": { + "summary": "Update Hero", + "operationId": "update_hero_heroes__hero_id__patch", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroUpdate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + }, + "components": { + "schemas": { + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + } + }, + }, + "HeroCreate": { + "title": "HeroCreate", + "required": ["name", "secret_name"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + }, + }, + "HeroRead": { + "title": "HeroRead", + "required": ["name", "secret_name", "id"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + "id": {"title": "Id", "type": "integer"}, + }, + }, + "HeroUpdate": { + "title": "HeroUpdate", + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + } + }, + } diff --git a/tests/test_tutorial/test_fastapi/test_session_with_dependency/test_tutorial001_py39.py b/tests/test_tutorial/test_fastapi/test_session_with_dependency/test_tutorial001_py39.py new file mode 100644 index 00000000..5b911c84 --- /dev/null +++ b/tests/test_tutorial/test_fastapi/test_session_with_dependency/test_tutorial001_py39.py @@ -0,0 +1,333 @@ +from fastapi.testclient import TestClient +from sqlmodel import create_engine +from sqlmodel.pool import StaticPool + +from ....conftest import needs_py39 + + +@needs_py39 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.fastapi.session_with_dependency import ( + tutorial001_py39 as mod, + ) + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine( + mod.sqlite_url, connect_args=mod.connect_args, poolclass=StaticPool + ) + + with TestClient(mod.app) as client: + hero1_data = {"name": "Deadpond", "secret_name": "Dive Wilson"} + hero2_data = { + "name": "Spider-Boy", + "secret_name": "Pedro Parqueador", + "id": 9000, + } + hero3_data = { + "name": "Rusty-Man", + "secret_name": "Tommy Sharp", + "age": 48, + } + response = client.post("/heroes/", json=hero1_data) + assert response.status_code == 200, response.text + response = client.post("/heroes/", json=hero2_data) + assert response.status_code == 200, response.text + hero2 = response.json() + hero2_id = hero2["id"] + response = client.post("/heroes/", json=hero3_data) + assert response.status_code == 200, response.text + response = client.get(f"/heroes/{hero2_id}") + assert response.status_code == 200, response.text + response = client.get("/heroes/9000") + assert response.status_code == 404, response.text + response = client.get("/heroes/") + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 3 + response = client.patch( + f"/heroes/{hero2_id}", json={"secret_name": "Spider-Youngster"} + ) + assert response.status_code == 200, response.text + response = client.patch("/heroes/9001", json={"name": "Dragon Cube X"}) + assert response.status_code == 404, response.text + + response = client.delete(f"/heroes/{hero2_id}") + assert response.status_code == 200, response.text + response = client.get("/heroes/") + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 2 + + response = client.delete("/heroes/9000") + assert response.status_code == 404, response.text + + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/heroes/": { + "get": { + "summary": "Read Heroes", + "operationId": "read_heroes_heroes__get", + "parameters": [ + { + "required": False, + "schema": { + "title": "Offset", + "type": "integer", + "default": 0, + }, + "name": "offset", + "in": "query", + }, + { + "required": False, + "schema": { + "title": "Limit", + "maximum": 100.0, + "type": "integer", + "default": 100, + }, + "name": "limit", + "in": "query", + }, + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "title": "Response Read Heroes Heroes Get", + "type": "array", + "items": { + "$ref": "#/components/schemas/HeroRead" + }, + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "post": { + "summary": "Create Hero", + "operationId": "create_hero_heroes__post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroCreate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + "/heroes/{hero_id}": { + "get": { + "summary": "Read Hero", + "operationId": "read_hero_heroes__hero_id__get", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "delete": { + "summary": "Delete Hero", + "operationId": "delete_hero_heroes__hero_id__delete", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "patch": { + "summary": "Update Hero", + "operationId": "update_hero_heroes__hero_id__patch", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroUpdate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + }, + "components": { + "schemas": { + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + } + }, + }, + "HeroCreate": { + "title": "HeroCreate", + "required": ["name", "secret_name"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + }, + }, + "HeroRead": { + "title": "HeroRead", + "required": ["name", "secret_name", "id"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + "id": {"title": "Id", "type": "integer"}, + }, + }, + "HeroUpdate": { + "title": "HeroUpdate", + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + } + }, + } diff --git a/tests/test_tutorial/test_fastapi/test_simple_hero_api/test_tutorial001_py310.py b/tests/test_tutorial/test_fastapi/test_simple_hero_api/test_tutorial001_py310.py new file mode 100644 index 00000000..d85d9ee5 --- /dev/null +++ b/tests/test_tutorial/test_fastapi/test_simple_hero_api/test_tutorial001_py310.py @@ -0,0 +1,150 @@ +from fastapi.testclient import TestClient +from sqlmodel import create_engine +from sqlmodel.pool import StaticPool + +from ....conftest import needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.fastapi.simple_hero_api import tutorial001_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine( + mod.sqlite_url, connect_args=mod.connect_args, poolclass=StaticPool + ) + + with TestClient(mod.app) as client: + hero1_data = {"name": "Deadpond", "secret_name": "Dive Wilson"} + hero2_data = { + "name": "Spider-Boy", + "secret_name": "Pedro Parqueador", + "id": 9000, + } + response = client.post("/heroes/", json=hero1_data) + data = response.json() + + assert response.status_code == 200, response.text + assert data["name"] == hero1_data["name"] + assert data["secret_name"] == hero1_data["secret_name"] + assert data["id"] is not None + assert data["age"] is None + + response = client.post("/heroes/", json=hero2_data) + data = response.json() + + assert response.status_code == 200, response.text + assert data["name"] == hero2_data["name"] + assert data["secret_name"] == hero2_data["secret_name"] + assert data["id"] == hero2_data["id"], ( + "Up to this point it's still possible to " + "set the ID of the hero in the request" + ) + assert data["age"] is None + + response = client.get("/heroes/") + data = response.json() + + assert response.status_code == 200, response.text + assert len(data) == 2 + assert data[0]["name"] == hero1_data["name"] + assert data[0]["secret_name"] == hero1_data["secret_name"] + assert data[1]["name"] == hero2_data["name"] + assert data[1]["secret_name"] == hero2_data["secret_name"] + assert data[1]["id"] == hero2_data["id"] + + response = client.get("/openapi.json") + data = response.json() + + assert response.status_code == 200, response.text + + assert data == { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/heroes/": { + "get": { + "summary": "Read Heroes", + "operationId": "read_heroes_heroes__get", + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + } + }, + }, + "post": { + "summary": "Create Hero", + "operationId": "create_hero_heroes__post", + "requestBody": { + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/Hero"} + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + } + }, + "components": { + "schemas": { + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + } + }, + }, + "Hero": { + "title": "Hero", + "required": ["name", "secret_name"], + "type": "object", + "properties": { + "id": {"title": "Id", "type": "integer"}, + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + } + }, + } diff --git a/tests/test_tutorial/test_fastapi/test_teams/test_tutorial001_py310.py b/tests/test_tutorial/test_fastapi/test_teams/test_tutorial001_py310.py new file mode 100644 index 00000000..6cec87a0 --- /dev/null +++ b/tests/test_tutorial/test_fastapi/test_teams/test_tutorial001_py310.py @@ -0,0 +1,595 @@ +from fastapi.testclient import TestClient +from sqlmodel import create_engine +from sqlmodel.pool import StaticPool + +from ....conftest import needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.fastapi.teams import tutorial001_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine( + mod.sqlite_url, connect_args=mod.connect_args, poolclass=StaticPool + ) + + with TestClient(mod.app) as client: + hero1_data = {"name": "Deadpond", "secret_name": "Dive Wilson"} + hero2_data = { + "name": "Spider-Boy", + "secret_name": "Pedro Parqueador", + "id": 9000, + } + hero3_data = { + "name": "Rusty-Man", + "secret_name": "Tommy Sharp", + "age": 48, + } + response = client.post("/heroes/", json=hero1_data) + assert response.status_code == 200, response.text + response = client.post("/heroes/", json=hero2_data) + assert response.status_code == 200, response.text + hero2 = response.json() + hero2_id = hero2["id"] + response = client.post("/heroes/", json=hero3_data) + assert response.status_code == 200, response.text + response = client.get(f"/heroes/{hero2_id}") + assert response.status_code == 200, response.text + response = client.get("/heroes/9000") + assert response.status_code == 404, response.text + response = client.get("/heroes/") + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 3 + response = client.patch( + f"/heroes/{hero2_id}", json={"secret_name": "Spider-Youngster"} + ) + assert response.status_code == 200, response.text + response = client.patch("/heroes/9001", json={"name": "Dragon Cube X"}) + assert response.status_code == 404, response.text + response = client.delete(f"/heroes/{hero2_id}") + assert response.status_code == 200, response.text + response = client.get("/heroes/") + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 2 + response = client.delete("/heroes/9000") + assert response.status_code == 404, response.text + + team_preventers = {"name": "Preventers", "headquarters": "Sharp Tower"} + team_z_force = {"name": "Z-Force", "headquarters": "Sister Margaret’s Bar"} + response = client.post("/teams/", json=team_preventers) + assert response.status_code == 200, response.text + team_preventers_data = response.json() + team_preventers_id = team_preventers_data["id"] + response = client.post("/teams/", json=team_z_force) + assert response.status_code == 200, response.text + team_z_force_data = response.json() + team_z_force_data["id"] + response = client.get("/teams/") + data = response.json() + assert len(data) == 2 + response = client.get(f"/teams/{team_preventers_id}") + data = response.json() + assert response.status_code == 200, response.text + assert data == team_preventers_data + response = client.get("/teams/9000") + assert response.status_code == 404, response.text + response = client.patch( + f"/teams/{team_preventers_id}", json={"headquarters": "Preventers Tower"} + ) + data = response.json() + assert response.status_code == 200, response.text + assert data["name"] == team_preventers["name"] + assert data["headquarters"] == "Preventers Tower" + response = client.patch("/teams/9000", json={"name": "Freedom League"}) + assert response.status_code == 404, response.text + response = client.delete(f"/teams/{team_preventers_id}") + assert response.status_code == 200, response.text + response = client.delete("/teams/9000") + assert response.status_code == 404, response.text + response = client.get("/teams/") + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 1 + + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/heroes/": { + "get": { + "summary": "Read Heroes", + "operationId": "read_heroes_heroes__get", + "parameters": [ + { + "required": False, + "schema": { + "title": "Offset", + "type": "integer", + "default": 0, + }, + "name": "offset", + "in": "query", + }, + { + "required": False, + "schema": { + "title": "Limit", + "maximum": 100.0, + "type": "integer", + "default": 100, + }, + "name": "limit", + "in": "query", + }, + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "title": "Response Read Heroes Heroes Get", + "type": "array", + "items": { + "$ref": "#/components/schemas/HeroRead" + }, + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "post": { + "summary": "Create Hero", + "operationId": "create_hero_heroes__post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroCreate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + "/heroes/{hero_id}": { + "get": { + "summary": "Read Hero", + "operationId": "read_hero_heroes__hero_id__get", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "delete": { + "summary": "Delete Hero", + "operationId": "delete_hero_heroes__hero_id__delete", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "patch": { + "summary": "Update Hero", + "operationId": "update_hero_heroes__hero_id__patch", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroUpdate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + "/teams/": { + "get": { + "summary": "Read Teams", + "operationId": "read_teams_teams__get", + "parameters": [ + { + "required": False, + "schema": { + "title": "Offset", + "type": "integer", + "default": 0, + }, + "name": "offset", + "in": "query", + }, + { + "required": False, + "schema": { + "title": "Limit", + "maximum": 100.0, + "type": "integer", + "default": 100, + }, + "name": "limit", + "in": "query", + }, + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "title": "Response Read Teams Teams Get", + "type": "array", + "items": { + "$ref": "#/components/schemas/TeamRead" + }, + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "post": { + "summary": "Create Team", + "operationId": "create_team_teams__post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TeamCreate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TeamRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + "/teams/{team_id}": { + "get": { + "summary": "Read Team", + "operationId": "read_team_teams__team_id__get", + "parameters": [ + { + "required": True, + "schema": {"title": "Team Id", "type": "integer"}, + "name": "team_id", + "in": "path", + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TeamRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "delete": { + "summary": "Delete Team", + "operationId": "delete_team_teams__team_id__delete", + "parameters": [ + { + "required": True, + "schema": {"title": "Team Id", "type": "integer"}, + "name": "team_id", + "in": "path", + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "patch": { + "summary": "Update Team", + "operationId": "update_team_teams__team_id__patch", + "parameters": [ + { + "required": True, + "schema": {"title": "Team Id", "type": "integer"}, + "name": "team_id", + "in": "path", + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TeamUpdate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TeamRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + }, + "components": { + "schemas": { + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + } + }, + }, + "HeroCreate": { + "title": "HeroCreate", + "required": ["name", "secret_name"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + "team_id": {"title": "Team Id", "type": "integer"}, + }, + }, + "HeroRead": { + "title": "HeroRead", + "required": ["name", "secret_name", "id"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + "team_id": {"title": "Team Id", "type": "integer"}, + "id": {"title": "Id", "type": "integer"}, + }, + }, + "HeroUpdate": { + "title": "HeroUpdate", + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + "team_id": {"title": "Team Id", "type": "integer"}, + }, + }, + "TeamCreate": { + "title": "TeamCreate", + "required": ["name", "headquarters"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "headquarters": {"title": "Headquarters", "type": "string"}, + }, + }, + "TeamRead": { + "title": "TeamRead", + "required": ["name", "headquarters", "id"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "headquarters": {"title": "Headquarters", "type": "string"}, + "id": {"title": "Id", "type": "integer"}, + }, + }, + "TeamUpdate": { + "title": "TeamUpdate", + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "headquarters": {"title": "Headquarters", "type": "string"}, + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + } + }, + } diff --git a/tests/test_tutorial/test_fastapi/test_teams/test_tutorial001_py39.py b/tests/test_tutorial/test_fastapi/test_teams/test_tutorial001_py39.py new file mode 100644 index 00000000..70279f5b --- /dev/null +++ b/tests/test_tutorial/test_fastapi/test_teams/test_tutorial001_py39.py @@ -0,0 +1,595 @@ +from fastapi.testclient import TestClient +from sqlmodel import create_engine +from sqlmodel.pool import StaticPool + +from ....conftest import needs_py39 + + +@needs_py39 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.fastapi.teams import tutorial001_py39 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine( + mod.sqlite_url, connect_args=mod.connect_args, poolclass=StaticPool + ) + + with TestClient(mod.app) as client: + hero1_data = {"name": "Deadpond", "secret_name": "Dive Wilson"} + hero2_data = { + "name": "Spider-Boy", + "secret_name": "Pedro Parqueador", + "id": 9000, + } + hero3_data = { + "name": "Rusty-Man", + "secret_name": "Tommy Sharp", + "age": 48, + } + response = client.post("/heroes/", json=hero1_data) + assert response.status_code == 200, response.text + response = client.post("/heroes/", json=hero2_data) + assert response.status_code == 200, response.text + hero2 = response.json() + hero2_id = hero2["id"] + response = client.post("/heroes/", json=hero3_data) + assert response.status_code == 200, response.text + response = client.get(f"/heroes/{hero2_id}") + assert response.status_code == 200, response.text + response = client.get("/heroes/9000") + assert response.status_code == 404, response.text + response = client.get("/heroes/") + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 3 + response = client.patch( + f"/heroes/{hero2_id}", json={"secret_name": "Spider-Youngster"} + ) + assert response.status_code == 200, response.text + response = client.patch("/heroes/9001", json={"name": "Dragon Cube X"}) + assert response.status_code == 404, response.text + response = client.delete(f"/heroes/{hero2_id}") + assert response.status_code == 200, response.text + response = client.get("/heroes/") + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 2 + response = client.delete("/heroes/9000") + assert response.status_code == 404, response.text + + team_preventers = {"name": "Preventers", "headquarters": "Sharp Tower"} + team_z_force = {"name": "Z-Force", "headquarters": "Sister Margaret’s Bar"} + response = client.post("/teams/", json=team_preventers) + assert response.status_code == 200, response.text + team_preventers_data = response.json() + team_preventers_id = team_preventers_data["id"] + response = client.post("/teams/", json=team_z_force) + assert response.status_code == 200, response.text + team_z_force_data = response.json() + team_z_force_data["id"] + response = client.get("/teams/") + data = response.json() + assert len(data) == 2 + response = client.get(f"/teams/{team_preventers_id}") + data = response.json() + assert response.status_code == 200, response.text + assert data == team_preventers_data + response = client.get("/teams/9000") + assert response.status_code == 404, response.text + response = client.patch( + f"/teams/{team_preventers_id}", json={"headquarters": "Preventers Tower"} + ) + data = response.json() + assert response.status_code == 200, response.text + assert data["name"] == team_preventers["name"] + assert data["headquarters"] == "Preventers Tower" + response = client.patch("/teams/9000", json={"name": "Freedom League"}) + assert response.status_code == 404, response.text + response = client.delete(f"/teams/{team_preventers_id}") + assert response.status_code == 200, response.text + response = client.delete("/teams/9000") + assert response.status_code == 404, response.text + response = client.get("/teams/") + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 1 + + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/heroes/": { + "get": { + "summary": "Read Heroes", + "operationId": "read_heroes_heroes__get", + "parameters": [ + { + "required": False, + "schema": { + "title": "Offset", + "type": "integer", + "default": 0, + }, + "name": "offset", + "in": "query", + }, + { + "required": False, + "schema": { + "title": "Limit", + "maximum": 100.0, + "type": "integer", + "default": 100, + }, + "name": "limit", + "in": "query", + }, + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "title": "Response Read Heroes Heroes Get", + "type": "array", + "items": { + "$ref": "#/components/schemas/HeroRead" + }, + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "post": { + "summary": "Create Hero", + "operationId": "create_hero_heroes__post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroCreate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + "/heroes/{hero_id}": { + "get": { + "summary": "Read Hero", + "operationId": "read_hero_heroes__hero_id__get", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "delete": { + "summary": "Delete Hero", + "operationId": "delete_hero_heroes__hero_id__delete", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "patch": { + "summary": "Update Hero", + "operationId": "update_hero_heroes__hero_id__patch", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroUpdate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + "/teams/": { + "get": { + "summary": "Read Teams", + "operationId": "read_teams_teams__get", + "parameters": [ + { + "required": False, + "schema": { + "title": "Offset", + "type": "integer", + "default": 0, + }, + "name": "offset", + "in": "query", + }, + { + "required": False, + "schema": { + "title": "Limit", + "maximum": 100.0, + "type": "integer", + "default": 100, + }, + "name": "limit", + "in": "query", + }, + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "title": "Response Read Teams Teams Get", + "type": "array", + "items": { + "$ref": "#/components/schemas/TeamRead" + }, + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "post": { + "summary": "Create Team", + "operationId": "create_team_teams__post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TeamCreate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TeamRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + "/teams/{team_id}": { + "get": { + "summary": "Read Team", + "operationId": "read_team_teams__team_id__get", + "parameters": [ + { + "required": True, + "schema": {"title": "Team Id", "type": "integer"}, + "name": "team_id", + "in": "path", + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TeamRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "delete": { + "summary": "Delete Team", + "operationId": "delete_team_teams__team_id__delete", + "parameters": [ + { + "required": True, + "schema": {"title": "Team Id", "type": "integer"}, + "name": "team_id", + "in": "path", + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "patch": { + "summary": "Update Team", + "operationId": "update_team_teams__team_id__patch", + "parameters": [ + { + "required": True, + "schema": {"title": "Team Id", "type": "integer"}, + "name": "team_id", + "in": "path", + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TeamUpdate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TeamRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + }, + "components": { + "schemas": { + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + } + }, + }, + "HeroCreate": { + "title": "HeroCreate", + "required": ["name", "secret_name"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + "team_id": {"title": "Team Id", "type": "integer"}, + }, + }, + "HeroRead": { + "title": "HeroRead", + "required": ["name", "secret_name", "id"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + "team_id": {"title": "Team Id", "type": "integer"}, + "id": {"title": "Id", "type": "integer"}, + }, + }, + "HeroUpdate": { + "title": "HeroUpdate", + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + "team_id": {"title": "Team Id", "type": "integer"}, + }, + }, + "TeamCreate": { + "title": "TeamCreate", + "required": ["name", "headquarters"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "headquarters": {"title": "Headquarters", "type": "string"}, + }, + }, + "TeamRead": { + "title": "TeamRead", + "required": ["name", "headquarters", "id"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "headquarters": {"title": "Headquarters", "type": "string"}, + "id": {"title": "Id", "type": "integer"}, + }, + }, + "TeamUpdate": { + "title": "TeamUpdate", + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "headquarters": {"title": "Headquarters", "type": "string"}, + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + } + }, + } diff --git a/tests/test_tutorial/test_fastapi/test_update/test_tutorial001_py310.py b/tests/test_tutorial/test_fastapi/test_update/test_tutorial001_py310.py new file mode 100644 index 00000000..cf56e3cb --- /dev/null +++ b/tests/test_tutorial/test_fastapi/test_update/test_tutorial001_py310.py @@ -0,0 +1,310 @@ +from fastapi.testclient import TestClient +from sqlmodel import create_engine +from sqlmodel.pool import StaticPool + +from ....conftest import needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.fastapi.update import tutorial001_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine( + mod.sqlite_url, connect_args=mod.connect_args, poolclass=StaticPool + ) + + with TestClient(mod.app) as client: + hero1_data = {"name": "Deadpond", "secret_name": "Dive Wilson"} + hero2_data = { + "name": "Spider-Boy", + "secret_name": "Pedro Parqueador", + "id": 9000, + } + hero3_data = { + "name": "Rusty-Man", + "secret_name": "Tommy Sharp", + "age": 48, + } + response = client.post("/heroes/", json=hero1_data) + assert response.status_code == 200, response.text + response = client.post("/heroes/", json=hero2_data) + assert response.status_code == 200, response.text + hero2 = response.json() + hero2_id = hero2["id"] + response = client.post("/heroes/", json=hero3_data) + assert response.status_code == 200, response.text + hero3 = response.json() + hero3_id = hero3["id"] + response = client.get(f"/heroes/{hero2_id}") + assert response.status_code == 200, response.text + response = client.get("/heroes/9000") + assert response.status_code == 404, response.text + response = client.get("/heroes/") + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 3 + + response = client.patch( + f"/heroes/{hero2_id}", json={"secret_name": "Spider-Youngster"} + ) + data = response.json() + assert response.status_code == 200, response.text + assert data["name"] == hero2_data["name"], "The name should not be set to none" + assert ( + data["secret_name"] == "Spider-Youngster" + ), "The secret name should be updated" + + response = client.patch(f"/heroes/{hero3_id}", json={"age": None}) + data = response.json() + assert response.status_code == 200, response.text + assert data["name"] == hero3_data["name"] + assert data["age"] is None, ( + "A field should be updatable to None, even if " "that's the default" + ) + + response = client.patch("/heroes/9001", json={"name": "Dragon Cube X"}) + assert response.status_code == 404, response.text + + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/heroes/": { + "get": { + "summary": "Read Heroes", + "operationId": "read_heroes_heroes__get", + "parameters": [ + { + "required": False, + "schema": { + "title": "Offset", + "type": "integer", + "default": 0, + }, + "name": "offset", + "in": "query", + }, + { + "required": False, + "schema": { + "title": "Limit", + "maximum": 100.0, + "type": "integer", + "default": 100, + }, + "name": "limit", + "in": "query", + }, + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "title": "Response Read Heroes Heroes Get", + "type": "array", + "items": { + "$ref": "#/components/schemas/HeroRead" + }, + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "post": { + "summary": "Create Hero", + "operationId": "create_hero_heroes__post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroCreate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + "/heroes/{hero_id}": { + "get": { + "summary": "Read Hero", + "operationId": "read_hero_heroes__hero_id__get", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "patch": { + "summary": "Update Hero", + "operationId": "update_hero_heroes__hero_id__patch", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroUpdate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + }, + "components": { + "schemas": { + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + } + }, + }, + "HeroCreate": { + "title": "HeroCreate", + "required": ["name", "secret_name"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + }, + }, + "HeroRead": { + "title": "HeroRead", + "required": ["name", "secret_name", "id"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + "id": {"title": "Id", "type": "integer"}, + }, + }, + "HeroUpdate": { + "title": "HeroUpdate", + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + } + }, + } diff --git a/tests/test_tutorial/test_fastapi/test_update/test_tutorial001_py39.py b/tests/test_tutorial/test_fastapi/test_update/test_tutorial001_py39.py new file mode 100644 index 00000000..b301ca3b --- /dev/null +++ b/tests/test_tutorial/test_fastapi/test_update/test_tutorial001_py39.py @@ -0,0 +1,310 @@ +from fastapi.testclient import TestClient +from sqlmodel import create_engine +from sqlmodel.pool import StaticPool + +from ....conftest import needs_py39 + + +@needs_py39 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.fastapi.update import tutorial001_py39 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine( + mod.sqlite_url, connect_args=mod.connect_args, poolclass=StaticPool + ) + + with TestClient(mod.app) as client: + hero1_data = {"name": "Deadpond", "secret_name": "Dive Wilson"} + hero2_data = { + "name": "Spider-Boy", + "secret_name": "Pedro Parqueador", + "id": 9000, + } + hero3_data = { + "name": "Rusty-Man", + "secret_name": "Tommy Sharp", + "age": 48, + } + response = client.post("/heroes/", json=hero1_data) + assert response.status_code == 200, response.text + response = client.post("/heroes/", json=hero2_data) + assert response.status_code == 200, response.text + hero2 = response.json() + hero2_id = hero2["id"] + response = client.post("/heroes/", json=hero3_data) + assert response.status_code == 200, response.text + hero3 = response.json() + hero3_id = hero3["id"] + response = client.get(f"/heroes/{hero2_id}") + assert response.status_code == 200, response.text + response = client.get("/heroes/9000") + assert response.status_code == 404, response.text + response = client.get("/heroes/") + assert response.status_code == 200, response.text + data = response.json() + assert len(data) == 3 + + response = client.patch( + f"/heroes/{hero2_id}", json={"secret_name": "Spider-Youngster"} + ) + data = response.json() + assert response.status_code == 200, response.text + assert data["name"] == hero2_data["name"], "The name should not be set to none" + assert ( + data["secret_name"] == "Spider-Youngster" + ), "The secret name should be updated" + + response = client.patch(f"/heroes/{hero3_id}", json={"age": None}) + data = response.json() + assert response.status_code == 200, response.text + assert data["name"] == hero3_data["name"] + assert data["age"] is None, ( + "A field should be updatable to None, even if " "that's the default" + ) + + response = client.patch("/heroes/9001", json={"name": "Dragon Cube X"}) + assert response.status_code == 404, response.text + + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/heroes/": { + "get": { + "summary": "Read Heroes", + "operationId": "read_heroes_heroes__get", + "parameters": [ + { + "required": False, + "schema": { + "title": "Offset", + "type": "integer", + "default": 0, + }, + "name": "offset", + "in": "query", + }, + { + "required": False, + "schema": { + "title": "Limit", + "maximum": 100.0, + "type": "integer", + "default": 100, + }, + "name": "limit", + "in": "query", + }, + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "title": "Response Read Heroes Heroes Get", + "type": "array", + "items": { + "$ref": "#/components/schemas/HeroRead" + }, + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "post": { + "summary": "Create Hero", + "operationId": "create_hero_heroes__post", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroCreate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + "/heroes/{hero_id}": { + "get": { + "summary": "Read Hero", + "operationId": "read_hero_heroes__hero_id__get", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "patch": { + "summary": "Update Hero", + "operationId": "update_hero_heroes__hero_id__patch", + "parameters": [ + { + "required": True, + "schema": {"title": "Hero Id", "type": "integer"}, + "name": "hero_id", + "in": "path", + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroUpdate" + } + } + }, + "required": True, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroRead" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + }, + "components": { + "schemas": { + "HTTPValidationError": { + "title": "HTTPValidationError", + "type": "object", + "properties": { + "detail": { + "title": "Detail", + "type": "array", + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + } + }, + }, + "HeroCreate": { + "title": "HeroCreate", + "required": ["name", "secret_name"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + }, + }, + "HeroRead": { + "title": "HeroRead", + "required": ["name", "secret_name", "id"], + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + "id": {"title": "Id", "type": "integer"}, + }, + }, + "HeroUpdate": { + "title": "HeroUpdate", + "type": "object", + "properties": { + "name": {"title": "Name", "type": "string"}, + "secret_name": {"title": "Secret Name", "type": "string"}, + "age": {"title": "Age", "type": "integer"}, + }, + }, + "ValidationError": { + "title": "ValidationError", + "required": ["loc", "msg", "type"], + "type": "object", + "properties": { + "loc": { + "title": "Location", + "type": "array", + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + }, + "msg": {"title": "Message", "type": "string"}, + "type": {"title": "Error Type", "type": "string"}, + }, + }, + } + }, + } diff --git a/tests/test_tutorial/test_indexes/test_tutorial001_py310.py b/tests/test_tutorial/test_indexes/test_tutorial001_py310.py new file mode 100644 index 00000000..cfee262b --- /dev/null +++ b/tests/test_tutorial/test_indexes/test_tutorial001_py310.py @@ -0,0 +1,46 @@ +from unittest.mock import patch + +from sqlalchemy import inspect +from sqlalchemy.engine.reflection import Inspector +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.indexes import tutorial001_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == [ + [{"secret_name": "Dive Wilson", "age": None, "id": 1, "name": "Deadpond"}] + ] + + insp: Inspector = inspect(mod.engine) + indexes = insp.get_indexes(str(mod.Hero.__tablename__)) + expected_indexes = [ + { + "name": "ix_hero_name", + "dialect_options": {}, + "column_names": ["name"], + "unique": 0, + }, + { + "name": "ix_hero_age", + "dialect_options": {}, + "column_names": ["age"], + "unique": 0, + }, + ] + for index in expected_indexes: + assert index in indexes, "This expected index should be in the indexes in DB" + # Now that this index was checked, remove it from the list of indexes + indexes.pop(indexes.index(index)) + assert len(indexes) == 0, "The database should only have the expected indexes" diff --git a/tests/test_tutorial/test_indexes/test_tutorial006.py b/tests/test_tutorial/test_indexes/test_tutorial002.py similarity index 100% rename from tests/test_tutorial/test_indexes/test_tutorial006.py rename to tests/test_tutorial/test_indexes/test_tutorial002.py diff --git a/tests/test_tutorial/test_indexes/test_tutorial002_py310.py b/tests/test_tutorial/test_indexes/test_tutorial002_py310.py new file mode 100644 index 00000000..089b6828 --- /dev/null +++ b/tests/test_tutorial/test_indexes/test_tutorial002_py310.py @@ -0,0 +1,47 @@ +from unittest.mock import patch + +from sqlalchemy import inspect +from sqlalchemy.engine.reflection import Inspector +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.indexes import tutorial002_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == [ + [{"name": "Tarantula", "secret_name": "Natalia Roman-on", "age": 32, "id": 4}], + [{"name": "Black Lion", "secret_name": "Trevor Challa", "age": 35, "id": 5}], + ] + + insp: Inspector = inspect(mod.engine) + indexes = insp.get_indexes(str(mod.Hero.__tablename__)) + expected_indexes = [ + { + "name": "ix_hero_name", + "dialect_options": {}, + "column_names": ["name"], + "unique": 0, + }, + { + "name": "ix_hero_age", + "dialect_options": {}, + "column_names": ["age"], + "unique": 0, + }, + ] + for index in expected_indexes: + assert index in indexes, "This expected index should be in the indexes in DB" + # Now that this index was checked, remove it from the list of indexes + indexes.pop(indexes.index(index)) + assert len(indexes) == 0, "The database should only have the expected indexes" diff --git a/tests/test_tutorial/test_insert/test_tutorial001_py310.py b/tests/test_tutorial/test_insert/test_tutorial001_py310.py new file mode 100644 index 00000000..47cbc4cd --- /dev/null +++ b/tests/test_tutorial/test_insert/test_tutorial001_py310.py @@ -0,0 +1,30 @@ +from sqlmodel import Session, create_engine, select + +from ...conftest import needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.insert import tutorial001_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + mod.main() + with Session(mod.engine) as session: + heroes = session.exec(select(mod.Hero)).all() + heroes_by_name = {hero.name: hero for hero in heroes} + deadpond = heroes_by_name["Deadpond"] + spider_boy = heroes_by_name["Spider-Boy"] + rusty_man = heroes_by_name["Rusty-Man"] + assert deadpond.name == "Deadpond" + assert deadpond.age is None + assert deadpond.id is not None + assert deadpond.secret_name == "Dive Wilson" + assert spider_boy.name == "Spider-Boy" + assert spider_boy.age is None + assert spider_boy.id is not None + assert spider_boy.secret_name == "Pedro Parqueador" + assert rusty_man.name == "Rusty-Man" + assert rusty_man.age == 48 + assert rusty_man.id is not None + assert rusty_man.secret_name == "Tommy Sharp" diff --git a/tests/test_tutorial/test_insert/test_tutorial002_py310.py b/tests/test_tutorial/test_insert/test_tutorial002_py310.py new file mode 100644 index 00000000..fb62810b --- /dev/null +++ b/tests/test_tutorial/test_insert/test_tutorial002_py310.py @@ -0,0 +1,30 @@ +from sqlmodel import Session, create_engine, select + +from ...conftest import needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.insert import tutorial002_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + mod.main() + with Session(mod.engine) as session: + heroes = session.exec(select(mod.Hero)).all() + heroes_by_name = {hero.name: hero for hero in heroes} + deadpond = heroes_by_name["Deadpond"] + spider_boy = heroes_by_name["Spider-Boy"] + rusty_man = heroes_by_name["Rusty-Man"] + assert deadpond.name == "Deadpond" + assert deadpond.age is None + assert deadpond.id is not None + assert deadpond.secret_name == "Dive Wilson" + assert spider_boy.name == "Spider-Boy" + assert spider_boy.age is None + assert spider_boy.id is not None + assert spider_boy.secret_name == "Pedro Parqueador" + assert rusty_man.name == "Rusty-Man" + assert rusty_man.age == 48 + assert rusty_man.id is not None + assert rusty_man.secret_name == "Tommy Sharp" diff --git a/tests/test_tutorial/test_insert/test_tutorial003_py310.py b/tests/test_tutorial/test_insert/test_tutorial003_py310.py new file mode 100644 index 00000000..5bca713e --- /dev/null +++ b/tests/test_tutorial/test_insert/test_tutorial003_py310.py @@ -0,0 +1,30 @@ +from sqlmodel import Session, create_engine, select + +from ...conftest import needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.insert import tutorial003_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + mod.main() + with Session(mod.engine) as session: + heroes = session.exec(select(mod.Hero)).all() + heroes_by_name = {hero.name: hero for hero in heroes} + deadpond = heroes_by_name["Deadpond"] + spider_boy = heroes_by_name["Spider-Boy"] + rusty_man = heroes_by_name["Rusty-Man"] + assert deadpond.name == "Deadpond" + assert deadpond.age is None + assert deadpond.id is not None + assert deadpond.secret_name == "Dive Wilson" + assert spider_boy.name == "Spider-Boy" + assert spider_boy.age is None + assert spider_boy.id is not None + assert spider_boy.secret_name == "Pedro Parqueador" + assert rusty_man.name == "Rusty-Man" + assert rusty_man.age == 48 + assert rusty_man.id is not None + assert rusty_man.secret_name == "Tommy Sharp" diff --git a/tests/test_tutorial/test_limit_and_offset/test_tutorial001_py310.py b/tests/test_tutorial/test_limit_and_offset/test_tutorial001_py310.py new file mode 100644 index 00000000..4f4974c8 --- /dev/null +++ b/tests/test_tutorial/test_limit_and_offset/test_tutorial001_py310.py @@ -0,0 +1,35 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + +expected_calls = [ + [ + [ + {"id": 1, "name": "Deadpond", "secret_name": "Dive Wilson", "age": None}, + { + "id": 2, + "name": "Spider-Boy", + "secret_name": "Pedro Parqueador", + "age": None, + }, + {"id": 3, "name": "Rusty-Man", "secret_name": "Tommy Sharp", "age": 48}, + ] + ] +] + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.offset_and_limit import tutorial001_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_limit_and_offset/test_tutorial002_py310.py b/tests/test_tutorial/test_limit_and_offset/test_tutorial002_py310.py new file mode 100644 index 00000000..1f86d196 --- /dev/null +++ b/tests/test_tutorial/test_limit_and_offset/test_tutorial002_py310.py @@ -0,0 +1,35 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + +expected_calls = [ + [ + [ + { + "id": 4, + "name": "Tarantula", + "secret_name": "Natalia Roman-on", + "age": 32, + }, + {"id": 5, "name": "Black Lion", "secret_name": "Trevor Challa", "age": 35}, + {"id": 6, "name": "Dr. Weird", "secret_name": "Steve Weird", "age": 36}, + ] + ] +] + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.offset_and_limit import tutorial002_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_limit_and_offset/test_tutorial003_py310.py b/tests/test_tutorial/test_limit_and_offset/test_tutorial003_py310.py new file mode 100644 index 00000000..99399915 --- /dev/null +++ b/tests/test_tutorial/test_limit_and_offset/test_tutorial003_py310.py @@ -0,0 +1,33 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + +expected_calls = [ + [ + [ + { + "id": 7, + "name": "Captain North America", + "secret_name": "Esteban Rogelios", + "age": 93, + } + ] + ] +] + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.offset_and_limit import tutorial003_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_limit_and_offset/test_tutorial004_py310.py b/tests/test_tutorial/test_limit_and_offset/test_tutorial004_py310.py new file mode 100644 index 00000000..4ca73658 --- /dev/null +++ b/tests/test_tutorial/test_limit_and_offset/test_tutorial004_py310.py @@ -0,0 +1,27 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.offset_and_limit import tutorial004_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == [ + [ + [ + {"name": "Dr. Weird", "secret_name": "Steve Weird", "age": 36, "id": 6}, + {"name": "Rusty-Man", "secret_name": "Tommy Sharp", "age": 48, "id": 3}, + ] + ] + ] diff --git a/tests/test_tutorial/test_many_to_many/test_tutorial001_py310.py b/tests/test_tutorial/test_many_to_many/test_tutorial001_py310.py new file mode 100644 index 00000000..19a1640b --- /dev/null +++ b/tests/test_tutorial/test_many_to_many/test_tutorial001_py310.py @@ -0,0 +1,50 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + +expected_calls = [ + [ + "Deadpond:", + {"id": 1, "secret_name": "Dive Wilson", "age": None, "name": "Deadpond"}, + ], + [ + "Deadpond teams:", + [ + {"id": 1, "name": "Z-Force", "headquarters": "Sister Margaret’s Bar"}, + {"id": 2, "name": "Preventers", "headquarters": "Sharp Tower"}, + ], + ], + [ + "Rusty-Man:", + {"id": 2, "secret_name": "Tommy Sharp", "age": 48, "name": "Rusty-Man"}, + ], + [ + "Rusty-Man Teams:", + [{"id": 2, "name": "Preventers", "headquarters": "Sharp Tower"}], + ], + [ + "Spider-Boy:", + {"id": 3, "secret_name": "Pedro Parqueador", "age": None, "name": "Spider-Boy"}, + ], + [ + "Spider-Boy Teams:", + [{"id": 2, "name": "Preventers", "headquarters": "Sharp Tower"}], + ], +] + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.many_to_many import tutorial001_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_many_to_many/test_tutorial001_py39.py b/tests/test_tutorial/test_many_to_many/test_tutorial001_py39.py new file mode 100644 index 00000000..23a7649f --- /dev/null +++ b/tests/test_tutorial/test_many_to_many/test_tutorial001_py39.py @@ -0,0 +1,50 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py39 + +expected_calls = [ + [ + "Deadpond:", + {"id": 1, "secret_name": "Dive Wilson", "age": None, "name": "Deadpond"}, + ], + [ + "Deadpond teams:", + [ + {"id": 1, "name": "Z-Force", "headquarters": "Sister Margaret’s Bar"}, + {"id": 2, "name": "Preventers", "headquarters": "Sharp Tower"}, + ], + ], + [ + "Rusty-Man:", + {"id": 2, "secret_name": "Tommy Sharp", "age": 48, "name": "Rusty-Man"}, + ], + [ + "Rusty-Man Teams:", + [{"id": 2, "name": "Preventers", "headquarters": "Sharp Tower"}], + ], + [ + "Spider-Boy:", + {"id": 3, "secret_name": "Pedro Parqueador", "age": None, "name": "Spider-Boy"}, + ], + [ + "Spider-Boy Teams:", + [{"id": 2, "name": "Preventers", "headquarters": "Sharp Tower"}], + ], +] + + +@needs_py39 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.many_to_many import tutorial001_py39 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_many_to_many/test_tutorial002_py310.py b/tests/test_tutorial/test_many_to_many/test_tutorial002_py310.py new file mode 100644 index 00000000..d480190f --- /dev/null +++ b/tests/test_tutorial/test_many_to_many/test_tutorial002_py310.py @@ -0,0 +1,77 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + +expected_calls = [ + [ + "Deadpond:", + {"id": 1, "secret_name": "Dive Wilson", "age": None, "name": "Deadpond"}, + ], + [ + "Deadpond teams:", + [ + {"id": 1, "name": "Z-Force", "headquarters": "Sister Margaret’s Bar"}, + {"id": 2, "name": "Preventers", "headquarters": "Sharp Tower"}, + ], + ], + [ + "Rusty-Man:", + {"id": 2, "secret_name": "Tommy Sharp", "age": 48, "name": "Rusty-Man"}, + ], + [ + "Rusty-Man Teams:", + [{"id": 2, "name": "Preventers", "headquarters": "Sharp Tower"}], + ], + [ + "Spider-Boy:", + {"id": 3, "secret_name": "Pedro Parqueador", "age": None, "name": "Spider-Boy"}, + ], + [ + "Spider-Boy Teams:", + [{"id": 2, "name": "Preventers", "headquarters": "Sharp Tower"}], + ], + [ + "Updated Spider-Boy's Teams:", + [ + {"id": 2, "name": "Preventers", "headquarters": "Sharp Tower"}, + {"id": 1, "name": "Z-Force", "headquarters": "Sister Margaret’s Bar"}, + ], + ], + [ + "Z-Force heroes:", + [ + {"id": 1, "secret_name": "Dive Wilson", "age": None, "name": "Deadpond"}, + { + "id": 3, + "secret_name": "Pedro Parqueador", + "age": None, + "name": "Spider-Boy", + }, + ], + ], + [ + "Reverted Z-Force's heroes:", + [{"id": 1, "secret_name": "Dive Wilson", "age": None, "name": "Deadpond"}], + ], + [ + "Reverted Spider-Boy's teams:", + [{"id": 2, "name": "Preventers", "headquarters": "Sharp Tower"}], + ], +] + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.many_to_many import tutorial002_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_many_to_many/test_tutorial002_py39.py b/tests/test_tutorial/test_many_to_many/test_tutorial002_py39.py new file mode 100644 index 00000000..85f649ac --- /dev/null +++ b/tests/test_tutorial/test_many_to_many/test_tutorial002_py39.py @@ -0,0 +1,77 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py39 + +expected_calls = [ + [ + "Deadpond:", + {"id": 1, "secret_name": "Dive Wilson", "age": None, "name": "Deadpond"}, + ], + [ + "Deadpond teams:", + [ + {"id": 1, "name": "Z-Force", "headquarters": "Sister Margaret’s Bar"}, + {"id": 2, "name": "Preventers", "headquarters": "Sharp Tower"}, + ], + ], + [ + "Rusty-Man:", + {"id": 2, "secret_name": "Tommy Sharp", "age": 48, "name": "Rusty-Man"}, + ], + [ + "Rusty-Man Teams:", + [{"id": 2, "name": "Preventers", "headquarters": "Sharp Tower"}], + ], + [ + "Spider-Boy:", + {"id": 3, "secret_name": "Pedro Parqueador", "age": None, "name": "Spider-Boy"}, + ], + [ + "Spider-Boy Teams:", + [{"id": 2, "name": "Preventers", "headquarters": "Sharp Tower"}], + ], + [ + "Updated Spider-Boy's Teams:", + [ + {"id": 2, "name": "Preventers", "headquarters": "Sharp Tower"}, + {"id": 1, "name": "Z-Force", "headquarters": "Sister Margaret’s Bar"}, + ], + ], + [ + "Z-Force heroes:", + [ + {"id": 1, "secret_name": "Dive Wilson", "age": None, "name": "Deadpond"}, + { + "id": 3, + "secret_name": "Pedro Parqueador", + "age": None, + "name": "Spider-Boy", + }, + ], + ], + [ + "Reverted Z-Force's heroes:", + [{"id": 1, "secret_name": "Dive Wilson", "age": None, "name": "Deadpond"}], + ], + [ + "Reverted Spider-Boy's teams:", + [{"id": 2, "name": "Preventers", "headquarters": "Sharp Tower"}], + ], +] + + +@needs_py39 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.many_to_many import tutorial002_py39 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_many_to_many/test_tutorial003_py310.py b/tests/test_tutorial/test_many_to_many/test_tutorial003_py310.py new file mode 100644 index 00000000..29918b35 --- /dev/null +++ b/tests/test_tutorial/test_many_to_many/test_tutorial003_py310.py @@ -0,0 +1,73 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + +expected_calls = [ + [ + "Z-Force hero:", + {"name": "Deadpond", "secret_name": "Dive Wilson", "id": 1, "age": None}, + "is training:", + False, + ], + [ + "Preventers hero:", + {"name": "Deadpond", "secret_name": "Dive Wilson", "id": 1, "age": None}, + "is training:", + True, + ], + [ + "Preventers hero:", + {"name": "Spider-Boy", "secret_name": "Pedro Parqueador", "id": 2, "age": None}, + "is training:", + True, + ], + [ + "Preventers hero:", + {"name": "Rusty-Man", "secret_name": "Tommy Sharp", "id": 3, "age": 48}, + "is training:", + False, + ], + [ + "Updated Spider-Boy's Teams:", + [ + {"team_id": 2, "is_training": True, "hero_id": 2}, + {"team_id": 1, "is_training": True, "hero_id": 2}, + ], + ], + [ + "Z-Force heroes:", + [ + {"team_id": 1, "is_training": False, "hero_id": 1}, + {"team_id": 1, "is_training": True, "hero_id": 2}, + ], + ], + [ + "Spider-Boy team:", + {"headquarters": "Sharp Tower", "id": 2, "name": "Preventers"}, + "is training:", + False, + ], + [ + "Spider-Boy team:", + {"headquarters": "Sister Margaret’s Bar", "id": 1, "name": "Z-Force"}, + "is training:", + True, + ], +] + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.many_to_many import tutorial003_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_many_to_many/test_tutorial003_py39.py b/tests/test_tutorial/test_many_to_many/test_tutorial003_py39.py new file mode 100644 index 00000000..5f1c87dd --- /dev/null +++ b/tests/test_tutorial/test_many_to_many/test_tutorial003_py39.py @@ -0,0 +1,73 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py39 + +expected_calls = [ + [ + "Z-Force hero:", + {"name": "Deadpond", "secret_name": "Dive Wilson", "id": 1, "age": None}, + "is training:", + False, + ], + [ + "Preventers hero:", + {"name": "Deadpond", "secret_name": "Dive Wilson", "id": 1, "age": None}, + "is training:", + True, + ], + [ + "Preventers hero:", + {"name": "Spider-Boy", "secret_name": "Pedro Parqueador", "id": 2, "age": None}, + "is training:", + True, + ], + [ + "Preventers hero:", + {"name": "Rusty-Man", "secret_name": "Tommy Sharp", "id": 3, "age": 48}, + "is training:", + False, + ], + [ + "Updated Spider-Boy's Teams:", + [ + {"team_id": 2, "is_training": True, "hero_id": 2}, + {"team_id": 1, "is_training": True, "hero_id": 2}, + ], + ], + [ + "Z-Force heroes:", + [ + {"team_id": 1, "is_training": False, "hero_id": 1}, + {"team_id": 1, "is_training": True, "hero_id": 2}, + ], + ], + [ + "Spider-Boy team:", + {"headquarters": "Sharp Tower", "id": 2, "name": "Preventers"}, + "is training:", + False, + ], + [ + "Spider-Boy team:", + {"headquarters": "Sister Margaret’s Bar", "id": 1, "name": "Z-Force"}, + "is training:", + True, + ], +] + + +@needs_py39 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.many_to_many import tutorial003_py39 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_one/test_tutorial001_py310.py b/tests/test_tutorial/test_one/test_tutorial001_py310.py new file mode 100644 index 00000000..6de87808 --- /dev/null +++ b/tests/test_tutorial/test_one/test_tutorial001_py310.py @@ -0,0 +1,30 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.one import tutorial001_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == [ + [ + "Hero:", + { + "name": "Tarantula", + "secret_name": "Natalia Roman-on", + "age": 32, + "id": 4, + }, + ] + ] diff --git a/tests/test_tutorial/test_one/test_tutorial002_py310.py b/tests/test_tutorial/test_one/test_tutorial002_py310.py new file mode 100644 index 00000000..afdfc545 --- /dev/null +++ b/tests/test_tutorial/test_one/test_tutorial002_py310.py @@ -0,0 +1,20 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.one import tutorial002_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == [["Hero:", None]] diff --git a/tests/test_tutorial/test_one/test_tutorial003_py310.py b/tests/test_tutorial/test_one/test_tutorial003_py310.py new file mode 100644 index 00000000..8eb8b861 --- /dev/null +++ b/tests/test_tutorial/test_one/test_tutorial003_py310.py @@ -0,0 +1,25 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.one import tutorial003_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == [ + [ + "Hero:", + {"name": "Deadpond", "secret_name": "Dive Wilson", "age": None, "id": 1}, + ] + ] diff --git a/tests/test_tutorial/test_one/test_tutorial004_py310.py b/tests/test_tutorial/test_one/test_tutorial004_py310.py new file mode 100644 index 00000000..cf365a4f --- /dev/null +++ b/tests/test_tutorial/test_one/test_tutorial004_py310.py @@ -0,0 +1,41 @@ +from unittest.mock import patch + +import pytest +from sqlalchemy.exc import MultipleResultsFound +from sqlmodel import Session, create_engine, delete + +from ...conftest import get_testing_print_function, needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.one import tutorial004_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + with pytest.raises(MultipleResultsFound): + mod.main() + with Session(mod.engine) as session: + # TODO: create delete() function + # TODO: add overloads for .exec() with delete object + session.exec(delete(mod.Hero)) + session.add(mod.Hero(name="Test Hero", secret_name="Secret Test Hero", age=24)) + session.commit() + + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.select_heroes() + assert calls == [ + [ + "Hero:", + { + "id": 1, + "name": "Test Hero", + "secret_name": "Secret Test Hero", + "age": 24, + }, + ] + ] diff --git a/tests/test_tutorial/test_one/test_tutorial005_py310.py b/tests/test_tutorial/test_one/test_tutorial005_py310.py new file mode 100644 index 00000000..f1fce7d7 --- /dev/null +++ b/tests/test_tutorial/test_one/test_tutorial005_py310.py @@ -0,0 +1,41 @@ +from unittest.mock import patch + +import pytest +from sqlalchemy.exc import NoResultFound +from sqlmodel import Session, create_engine, delete + +from ...conftest import get_testing_print_function, needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.one import tutorial005_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + with pytest.raises(NoResultFound): + mod.main() + with Session(mod.engine) as session: + # TODO: create delete() function + # TODO: add overloads for .exec() with delete object + session.exec(delete(mod.Hero)) + session.add(mod.Hero(name="Test Hero", secret_name="Secret Test Hero", age=24)) + session.commit() + + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.select_heroes() + assert calls == [ + [ + "Hero:", + { + "id": 1, + "name": "Test Hero", + "secret_name": "Secret Test Hero", + "age": 24, + }, + ] + ] diff --git a/tests/test_tutorial/test_one/test_tutorial006_py310.py b/tests/test_tutorial/test_one/test_tutorial006_py310.py new file mode 100644 index 00000000..ad8577c7 --- /dev/null +++ b/tests/test_tutorial/test_one/test_tutorial006_py310.py @@ -0,0 +1,25 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.one import tutorial006_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == [ + [ + "Hero:", + {"name": "Deadpond", "secret_name": "Dive Wilson", "age": None, "id": 1}, + ] + ] diff --git a/tests/test_tutorial/test_one/test_tutorial007_py310.py b/tests/test_tutorial/test_one/test_tutorial007_py310.py new file mode 100644 index 00000000..15b2306f --- /dev/null +++ b/tests/test_tutorial/test_one/test_tutorial007_py310.py @@ -0,0 +1,25 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.one import tutorial007_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == [ + [ + "Hero:", + {"name": "Deadpond", "secret_name": "Dive Wilson", "age": None, "id": 1}, + ] + ] diff --git a/tests/test_tutorial/test_one/test_tutorial008_py310.py b/tests/test_tutorial/test_one/test_tutorial008_py310.py new file mode 100644 index 00000000..c7d1fe55 --- /dev/null +++ b/tests/test_tutorial/test_one/test_tutorial008_py310.py @@ -0,0 +1,25 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.one import tutorial008_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == [ + [ + "Hero:", + {"name": "Deadpond", "secret_name": "Dive Wilson", "age": None, "id": 1}, + ] + ] diff --git a/tests/test_tutorial/test_one/test_tutorial009_py310.py b/tests/test_tutorial/test_one/test_tutorial009_py310.py new file mode 100644 index 00000000..8e9fda5f --- /dev/null +++ b/tests/test_tutorial/test_one/test_tutorial009_py310.py @@ -0,0 +1,20 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.one import tutorial009_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == [["Hero:", None]] diff --git a/tests/test_tutorial/test_relationship_attributes/test_back_populates/test_tutorial001_py310.py b/tests/test_tutorial/test_relationship_attributes/test_back_populates/test_tutorial001_py310.py new file mode 100644 index 00000000..384056ad --- /dev/null +++ b/tests/test_tutorial/test_relationship_attributes/test_back_populates/test_tutorial001_py310.py @@ -0,0 +1,290 @@ +from unittest.mock import patch + +import pytest +from sqlalchemy.exc import SAWarning +from sqlmodel import create_engine + +from ....conftest import get_testing_print_function, needs_py310 + +expected_calls = [ + [ + "Created hero:", + { + "age": None, + "id": 1, + "secret_name": "Dive Wilson", + "team_id": 1, + "name": "Deadpond", + }, + ], + [ + "Created hero:", + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 2, + "name": "Rusty-Man", + }, + ], + [ + "Created hero:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": None, + "name": "Spider-Boy", + }, + ], + [ + "Updated hero:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 2, + "name": "Spider-Boy", + }, + ], + [ + "Team Wakaland:", + {"headquarters": "Wakaland Capital City", "id": 3, "name": "Wakaland"}, + ], + [ + "Preventers new hero:", + { + "age": 32, + "id": 6, + "secret_name": "Natalia Roman-on", + "team_id": 2, + "name": "Tarantula", + }, + ], + [ + "Preventers new hero:", + { + "age": 36, + "id": 7, + "secret_name": "Steve Weird", + "team_id": 2, + "name": "Dr. Weird", + }, + ], + [ + "Preventers new hero:", + { + "age": 93, + "id": 8, + "secret_name": "Esteban Rogelios", + "team_id": 2, + "name": "Captain North America", + }, + ], + [ + "Preventers heroes:", + [ + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 2, + "name": "Rusty-Man", + }, + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 2, + "name": "Spider-Boy", + }, + { + "age": 32, + "id": 6, + "secret_name": "Natalia Roman-on", + "team_id": 2, + "name": "Tarantula", + }, + { + "age": 36, + "id": 7, + "secret_name": "Steve Weird", + "team_id": 2, + "name": "Dr. Weird", + }, + { + "age": 93, + "id": 8, + "secret_name": "Esteban Rogelios", + "team_id": 2, + "name": "Captain North America", + }, + ], + ], + [ + "Hero Spider-Boy:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 2, + "name": "Spider-Boy", + }, + ], + [ + "Preventers Team:", + {"headquarters": "Sharp Tower", "id": 2, "name": "Preventers"}, + ], + [ + "Preventers Team Heroes:", + [ + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 2, + "name": "Rusty-Man", + }, + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 2, + "name": "Spider-Boy", + }, + { + "age": 32, + "id": 6, + "secret_name": "Natalia Roman-on", + "team_id": 2, + "name": "Tarantula", + }, + { + "age": 36, + "id": 7, + "secret_name": "Steve Weird", + "team_id": 2, + "name": "Dr. Weird", + }, + { + "age": 93, + "id": 8, + "secret_name": "Esteban Rogelios", + "team_id": 2, + "name": "Captain North America", + }, + ], + ], + [ + "Spider-Boy without team:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 2, + "name": "Spider-Boy", + }, + ], + [ + "Preventers Team Heroes again:", + [ + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 2, + "name": "Rusty-Man", + }, + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 2, + "name": "Spider-Boy", + }, + { + "age": 32, + "id": 6, + "secret_name": "Natalia Roman-on", + "team_id": 2, + "name": "Tarantula", + }, + { + "age": 36, + "id": 7, + "secret_name": "Steve Weird", + "team_id": 2, + "name": "Dr. Weird", + }, + { + "age": 93, + "id": 8, + "secret_name": "Esteban Rogelios", + "team_id": 2, + "name": "Captain North America", + }, + ], + ], + ["After committing"], + [ + "Spider-Boy after commit:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": None, + "name": "Spider-Boy", + }, + ], + [ + "Preventers Team Heroes after commit:", + [ + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 2, + "name": "Rusty-Man", + }, + { + "age": 32, + "id": 6, + "secret_name": "Natalia Roman-on", + "team_id": 2, + "name": "Tarantula", + }, + { + "age": 36, + "id": 7, + "secret_name": "Steve Weird", + "team_id": 2, + "name": "Dr. Weird", + }, + { + "age": 93, + "id": 8, + "secret_name": "Esteban Rogelios", + "team_id": 2, + "name": "Captain North America", + }, + ], + ], +] + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.relationship_attributes.back_populates import ( + tutorial001_py310 as mod, + ) + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + with pytest.warns(SAWarning): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_relationship_attributes/test_back_populates/test_tutorial001_py39.py b/tests/test_tutorial/test_relationship_attributes/test_back_populates/test_tutorial001_py39.py new file mode 100644 index 00000000..0597a88e --- /dev/null +++ b/tests/test_tutorial/test_relationship_attributes/test_back_populates/test_tutorial001_py39.py @@ -0,0 +1,290 @@ +from unittest.mock import patch + +import pytest +from sqlalchemy.exc import SAWarning +from sqlmodel import create_engine + +from ....conftest import get_testing_print_function, needs_py39 + +expected_calls = [ + [ + "Created hero:", + { + "age": None, + "id": 1, + "secret_name": "Dive Wilson", + "team_id": 1, + "name": "Deadpond", + }, + ], + [ + "Created hero:", + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 2, + "name": "Rusty-Man", + }, + ], + [ + "Created hero:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": None, + "name": "Spider-Boy", + }, + ], + [ + "Updated hero:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 2, + "name": "Spider-Boy", + }, + ], + [ + "Team Wakaland:", + {"headquarters": "Wakaland Capital City", "id": 3, "name": "Wakaland"}, + ], + [ + "Preventers new hero:", + { + "age": 32, + "id": 6, + "secret_name": "Natalia Roman-on", + "team_id": 2, + "name": "Tarantula", + }, + ], + [ + "Preventers new hero:", + { + "age": 36, + "id": 7, + "secret_name": "Steve Weird", + "team_id": 2, + "name": "Dr. Weird", + }, + ], + [ + "Preventers new hero:", + { + "age": 93, + "id": 8, + "secret_name": "Esteban Rogelios", + "team_id": 2, + "name": "Captain North America", + }, + ], + [ + "Preventers heroes:", + [ + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 2, + "name": "Rusty-Man", + }, + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 2, + "name": "Spider-Boy", + }, + { + "age": 32, + "id": 6, + "secret_name": "Natalia Roman-on", + "team_id": 2, + "name": "Tarantula", + }, + { + "age": 36, + "id": 7, + "secret_name": "Steve Weird", + "team_id": 2, + "name": "Dr. Weird", + }, + { + "age": 93, + "id": 8, + "secret_name": "Esteban Rogelios", + "team_id": 2, + "name": "Captain North America", + }, + ], + ], + [ + "Hero Spider-Boy:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 2, + "name": "Spider-Boy", + }, + ], + [ + "Preventers Team:", + {"headquarters": "Sharp Tower", "id": 2, "name": "Preventers"}, + ], + [ + "Preventers Team Heroes:", + [ + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 2, + "name": "Rusty-Man", + }, + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 2, + "name": "Spider-Boy", + }, + { + "age": 32, + "id": 6, + "secret_name": "Natalia Roman-on", + "team_id": 2, + "name": "Tarantula", + }, + { + "age": 36, + "id": 7, + "secret_name": "Steve Weird", + "team_id": 2, + "name": "Dr. Weird", + }, + { + "age": 93, + "id": 8, + "secret_name": "Esteban Rogelios", + "team_id": 2, + "name": "Captain North America", + }, + ], + ], + [ + "Spider-Boy without team:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 2, + "name": "Spider-Boy", + }, + ], + [ + "Preventers Team Heroes again:", + [ + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 2, + "name": "Rusty-Man", + }, + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 2, + "name": "Spider-Boy", + }, + { + "age": 32, + "id": 6, + "secret_name": "Natalia Roman-on", + "team_id": 2, + "name": "Tarantula", + }, + { + "age": 36, + "id": 7, + "secret_name": "Steve Weird", + "team_id": 2, + "name": "Dr. Weird", + }, + { + "age": 93, + "id": 8, + "secret_name": "Esteban Rogelios", + "team_id": 2, + "name": "Captain North America", + }, + ], + ], + ["After committing"], + [ + "Spider-Boy after commit:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": None, + "name": "Spider-Boy", + }, + ], + [ + "Preventers Team Heroes after commit:", + [ + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 2, + "name": "Rusty-Man", + }, + { + "age": 32, + "id": 6, + "secret_name": "Natalia Roman-on", + "team_id": 2, + "name": "Tarantula", + }, + { + "age": 36, + "id": 7, + "secret_name": "Steve Weird", + "team_id": 2, + "name": "Dr. Weird", + }, + { + "age": 93, + "id": 8, + "secret_name": "Esteban Rogelios", + "team_id": 2, + "name": "Captain North America", + }, + ], + ], +] + + +@needs_py39 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.relationship_attributes.back_populates import ( + tutorial001_py39 as mod, + ) + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + with pytest.warns(SAWarning): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_relationship_attributes/test_back_populates/test_tutorial002_py310.py b/tests/test_tutorial/test_relationship_attributes/test_back_populates/test_tutorial002_py310.py new file mode 100644 index 00000000..50a891f3 --- /dev/null +++ b/tests/test_tutorial/test_relationship_attributes/test_back_populates/test_tutorial002_py310.py @@ -0,0 +1,280 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ....conftest import get_testing_print_function, needs_py310 + +expected_calls = [ + [ + "Created hero:", + { + "age": None, + "id": 1, + "secret_name": "Dive Wilson", + "team_id": 1, + "name": "Deadpond", + }, + ], + [ + "Created hero:", + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 2, + "name": "Rusty-Man", + }, + ], + [ + "Created hero:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": None, + "name": "Spider-Boy", + }, + ], + [ + "Updated hero:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 2, + "name": "Spider-Boy", + }, + ], + [ + "Team Wakaland:", + {"id": 3, "name": "Wakaland", "headquarters": "Wakaland Capital City"}, + ], + [ + "Preventers new hero:", + { + "age": 32, + "id": 6, + "secret_name": "Natalia Roman-on", + "team_id": 2, + "name": "Tarantula", + }, + ], + [ + "Preventers new hero:", + { + "age": 36, + "id": 7, + "secret_name": "Steve Weird", + "team_id": 2, + "name": "Dr. Weird", + }, + ], + [ + "Preventers new hero:", + { + "age": 93, + "id": 8, + "secret_name": "Esteban Rogelios", + "team_id": 2, + "name": "Captain North America", + }, + ], + [ + "Preventers heroes:", + [ + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 2, + "name": "Rusty-Man", + }, + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 2, + "name": "Spider-Boy", + }, + { + "age": 32, + "id": 6, + "secret_name": "Natalia Roman-on", + "team_id": 2, + "name": "Tarantula", + }, + { + "age": 36, + "id": 7, + "secret_name": "Steve Weird", + "team_id": 2, + "name": "Dr. Weird", + }, + { + "age": 93, + "id": 8, + "secret_name": "Esteban Rogelios", + "team_id": 2, + "name": "Captain North America", + }, + ], + ], + [ + "Hero Spider-Boy:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 2, + "name": "Spider-Boy", + }, + ], + [ + "Preventers Team:", + {"id": 2, "name": "Preventers", "headquarters": "Sharp Tower"}, + ], + [ + "Preventers Team Heroes:", + [ + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 2, + "name": "Rusty-Man", + }, + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 2, + "name": "Spider-Boy", + }, + { + "age": 32, + "id": 6, + "secret_name": "Natalia Roman-on", + "team_id": 2, + "name": "Tarantula", + }, + { + "age": 36, + "id": 7, + "secret_name": "Steve Weird", + "team_id": 2, + "name": "Dr. Weird", + }, + { + "age": 93, + "id": 8, + "secret_name": "Esteban Rogelios", + "team_id": 2, + "name": "Captain North America", + }, + ], + ], + [ + "Spider-Boy without team:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 2, + "name": "Spider-Boy", + }, + ], + [ + "Preventers Team Heroes again:", + [ + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 2, + "name": "Rusty-Man", + }, + { + "age": 32, + "id": 6, + "secret_name": "Natalia Roman-on", + "team_id": 2, + "name": "Tarantula", + }, + { + "age": 36, + "id": 7, + "secret_name": "Steve Weird", + "team_id": 2, + "name": "Dr. Weird", + }, + { + "age": 93, + "id": 8, + "secret_name": "Esteban Rogelios", + "team_id": 2, + "name": "Captain North America", + }, + ], + ], + ["After committing"], + [ + "Spider-Boy after commit:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": None, + "name": "Spider-Boy", + }, + ], + [ + "Preventers Team Heroes after commit:", + [ + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 2, + "name": "Rusty-Man", + }, + { + "age": 32, + "id": 6, + "secret_name": "Natalia Roman-on", + "team_id": 2, + "name": "Tarantula", + }, + { + "age": 36, + "id": 7, + "secret_name": "Steve Weird", + "team_id": 2, + "name": "Dr. Weird", + }, + { + "age": 93, + "id": 8, + "secret_name": "Esteban Rogelios", + "team_id": 2, + "name": "Captain North America", + }, + ], + ], +] + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.relationship_attributes.back_populates import ( + tutorial002_py310 as mod, + ) + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_relationship_attributes/test_back_populates/test_tutorial002_py39.py b/tests/test_tutorial/test_relationship_attributes/test_back_populates/test_tutorial002_py39.py new file mode 100644 index 00000000..3da6ce4a --- /dev/null +++ b/tests/test_tutorial/test_relationship_attributes/test_back_populates/test_tutorial002_py39.py @@ -0,0 +1,280 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ....conftest import get_testing_print_function, needs_py39 + +expected_calls = [ + [ + "Created hero:", + { + "age": None, + "id": 1, + "secret_name": "Dive Wilson", + "team_id": 1, + "name": "Deadpond", + }, + ], + [ + "Created hero:", + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 2, + "name": "Rusty-Man", + }, + ], + [ + "Created hero:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": None, + "name": "Spider-Boy", + }, + ], + [ + "Updated hero:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 2, + "name": "Spider-Boy", + }, + ], + [ + "Team Wakaland:", + {"id": 3, "name": "Wakaland", "headquarters": "Wakaland Capital City"}, + ], + [ + "Preventers new hero:", + { + "age": 32, + "id": 6, + "secret_name": "Natalia Roman-on", + "team_id": 2, + "name": "Tarantula", + }, + ], + [ + "Preventers new hero:", + { + "age": 36, + "id": 7, + "secret_name": "Steve Weird", + "team_id": 2, + "name": "Dr. Weird", + }, + ], + [ + "Preventers new hero:", + { + "age": 93, + "id": 8, + "secret_name": "Esteban Rogelios", + "team_id": 2, + "name": "Captain North America", + }, + ], + [ + "Preventers heroes:", + [ + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 2, + "name": "Rusty-Man", + }, + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 2, + "name": "Spider-Boy", + }, + { + "age": 32, + "id": 6, + "secret_name": "Natalia Roman-on", + "team_id": 2, + "name": "Tarantula", + }, + { + "age": 36, + "id": 7, + "secret_name": "Steve Weird", + "team_id": 2, + "name": "Dr. Weird", + }, + { + "age": 93, + "id": 8, + "secret_name": "Esteban Rogelios", + "team_id": 2, + "name": "Captain North America", + }, + ], + ], + [ + "Hero Spider-Boy:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 2, + "name": "Spider-Boy", + }, + ], + [ + "Preventers Team:", + {"id": 2, "name": "Preventers", "headquarters": "Sharp Tower"}, + ], + [ + "Preventers Team Heroes:", + [ + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 2, + "name": "Rusty-Man", + }, + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 2, + "name": "Spider-Boy", + }, + { + "age": 32, + "id": 6, + "secret_name": "Natalia Roman-on", + "team_id": 2, + "name": "Tarantula", + }, + { + "age": 36, + "id": 7, + "secret_name": "Steve Weird", + "team_id": 2, + "name": "Dr. Weird", + }, + { + "age": 93, + "id": 8, + "secret_name": "Esteban Rogelios", + "team_id": 2, + "name": "Captain North America", + }, + ], + ], + [ + "Spider-Boy without team:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 2, + "name": "Spider-Boy", + }, + ], + [ + "Preventers Team Heroes again:", + [ + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 2, + "name": "Rusty-Man", + }, + { + "age": 32, + "id": 6, + "secret_name": "Natalia Roman-on", + "team_id": 2, + "name": "Tarantula", + }, + { + "age": 36, + "id": 7, + "secret_name": "Steve Weird", + "team_id": 2, + "name": "Dr. Weird", + }, + { + "age": 93, + "id": 8, + "secret_name": "Esteban Rogelios", + "team_id": 2, + "name": "Captain North America", + }, + ], + ], + ["After committing"], + [ + "Spider-Boy after commit:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": None, + "name": "Spider-Boy", + }, + ], + [ + "Preventers Team Heroes after commit:", + [ + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 2, + "name": "Rusty-Man", + }, + { + "age": 32, + "id": 6, + "secret_name": "Natalia Roman-on", + "team_id": 2, + "name": "Tarantula", + }, + { + "age": 36, + "id": 7, + "secret_name": "Steve Weird", + "team_id": 2, + "name": "Dr. Weird", + }, + { + "age": 93, + "id": 8, + "secret_name": "Esteban Rogelios", + "team_id": 2, + "name": "Captain North America", + }, + ], + ], +] + + +@needs_py39 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.relationship_attributes.back_populates import ( + tutorial002_py39 as mod, + ) + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_relationship_attributes/test_back_populates/test_tutorial003_py310.py b/tests/test_tutorial/test_relationship_attributes/test_back_populates/test_tutorial003_py310.py new file mode 100644 index 00000000..82e0c1c0 --- /dev/null +++ b/tests/test_tutorial/test_relationship_attributes/test_back_populates/test_tutorial003_py310.py @@ -0,0 +1,21 @@ +from sqlalchemy import inspect +from sqlalchemy.engine.reflection import Inspector +from sqlmodel import create_engine + +from ....conftest import needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.relationship_attributes.back_populates import ( + tutorial003_py310 as mod, + ) + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + mod.main() + insp: Inspector = inspect(mod.engine) + assert insp.has_table(str(mod.Hero.__tablename__)) + assert insp.has_table(str(mod.Weapon.__tablename__)) + assert insp.has_table(str(mod.Power.__tablename__)) + assert insp.has_table(str(mod.Team.__tablename__)) diff --git a/tests/test_tutorial/test_relationship_attributes/test_back_populates/test_tutorial003_py39.py b/tests/test_tutorial/test_relationship_attributes/test_back_populates/test_tutorial003_py39.py new file mode 100644 index 00000000..d6059cb4 --- /dev/null +++ b/tests/test_tutorial/test_relationship_attributes/test_back_populates/test_tutorial003_py39.py @@ -0,0 +1,21 @@ +from sqlalchemy import inspect +from sqlalchemy.engine.reflection import Inspector +from sqlmodel import create_engine + +from ....conftest import needs_py39 + + +@needs_py39 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.relationship_attributes.back_populates import ( + tutorial003_py39 as mod, + ) + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + mod.main() + insp: Inspector = inspect(mod.engine) + assert insp.has_table(str(mod.Hero.__tablename__)) + assert insp.has_table(str(mod.Weapon.__tablename__)) + assert insp.has_table(str(mod.Power.__tablename__)) + assert insp.has_table(str(mod.Team.__tablename__)) diff --git a/tests/test_tutorial/test_relationship_attributes/test_create_and_update_relationships/test_tutorial001_py310.py b/tests/test_tutorial/test_relationship_attributes/test_create_and_update_relationships/test_tutorial001_py310.py new file mode 100644 index 00000000..c239b6d5 --- /dev/null +++ b/tests/test_tutorial/test_relationship_attributes/test_create_and_update_relationships/test_tutorial001_py310.py @@ -0,0 +1,99 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ....conftest import get_testing_print_function, needs_py310 + +expected_calls = [ + [ + "Created hero:", + { + "age": None, + "id": 1, + "secret_name": "Dive Wilson", + "team_id": 1, + "name": "Deadpond", + }, + ], + [ + "Created hero:", + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 2, + "name": "Rusty-Man", + }, + ], + [ + "Created hero:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": None, + "name": "Spider-Boy", + }, + ], + [ + "Updated hero:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 2, + "name": "Spider-Boy", + }, + ], + [ + "Team Wakaland:", + {"id": 3, "headquarters": "Wakaland Capital City", "name": "Wakaland"}, + ], + [ + "Preventers new hero:", + { + "age": 32, + "id": 6, + "secret_name": "Natalia Roman-on", + "team_id": 2, + "name": "Tarantula", + }, + ], + [ + "Preventers new hero:", + { + "age": 36, + "id": 7, + "secret_name": "Steve Weird", + "team_id": 2, + "name": "Dr. Weird", + }, + ], + [ + "Preventers new hero:", + { + "age": 93, + "id": 8, + "secret_name": "Esteban Rogelios", + "team_id": 2, + "name": "Captain North America", + }, + ], +] + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.relationship_attributes.create_and_update_relationships import ( + tutorial001_py310 as mod, + ) + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_relationship_attributes/test_create_and_update_relationships/test_tutorial001_py39.py b/tests/test_tutorial/test_relationship_attributes/test_create_and_update_relationships/test_tutorial001_py39.py new file mode 100644 index 00000000..c569eed0 --- /dev/null +++ b/tests/test_tutorial/test_relationship_attributes/test_create_and_update_relationships/test_tutorial001_py39.py @@ -0,0 +1,99 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ....conftest import get_testing_print_function, needs_py39 + +expected_calls = [ + [ + "Created hero:", + { + "age": None, + "id": 1, + "secret_name": "Dive Wilson", + "team_id": 1, + "name": "Deadpond", + }, + ], + [ + "Created hero:", + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 2, + "name": "Rusty-Man", + }, + ], + [ + "Created hero:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": None, + "name": "Spider-Boy", + }, + ], + [ + "Updated hero:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 2, + "name": "Spider-Boy", + }, + ], + [ + "Team Wakaland:", + {"id": 3, "headquarters": "Wakaland Capital City", "name": "Wakaland"}, + ], + [ + "Preventers new hero:", + { + "age": 32, + "id": 6, + "secret_name": "Natalia Roman-on", + "team_id": 2, + "name": "Tarantula", + }, + ], + [ + "Preventers new hero:", + { + "age": 36, + "id": 7, + "secret_name": "Steve Weird", + "team_id": 2, + "name": "Dr. Weird", + }, + ], + [ + "Preventers new hero:", + { + "age": 93, + "id": 8, + "secret_name": "Esteban Rogelios", + "team_id": 2, + "name": "Captain North America", + }, + ], +] + + +@needs_py39 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.relationship_attributes.create_and_update_relationships import ( + tutorial001_py39 as mod, + ) + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_relationship_attributes/test_define_relationship_attributes/test_tutorial001_py310.py b/tests/test_tutorial/test_relationship_attributes/test_define_relationship_attributes/test_tutorial001_py310.py new file mode 100644 index 00000000..f595dcaa --- /dev/null +++ b/tests/test_tutorial/test_relationship_attributes/test_define_relationship_attributes/test_tutorial001_py310.py @@ -0,0 +1,55 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ....conftest import get_testing_print_function, needs_py310 + +expected_calls = [ + [ + "Created hero:", + { + "name": "Deadpond", + "age": None, + "team_id": 1, + "id": 1, + "secret_name": "Dive Wilson", + }, + ], + [ + "Created hero:", + { + "name": "Rusty-Man", + "age": 48, + "team_id": 2, + "id": 2, + "secret_name": "Tommy Sharp", + }, + ], + [ + "Created hero:", + { + "name": "Spider-Boy", + "age": None, + "team_id": None, + "id": 3, + "secret_name": "Pedro Parqueador", + }, + ], +] + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.relationship_attributes.define_relationship_attributes import ( + tutorial001_py310 as mod, + ) + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_relationship_attributes/test_define_relationship_attributes/test_tutorial001_py39.py b/tests/test_tutorial/test_relationship_attributes/test_define_relationship_attributes/test_tutorial001_py39.py new file mode 100644 index 00000000..d54c610d --- /dev/null +++ b/tests/test_tutorial/test_relationship_attributes/test_define_relationship_attributes/test_tutorial001_py39.py @@ -0,0 +1,55 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ....conftest import get_testing_print_function, needs_py39 + +expected_calls = [ + [ + "Created hero:", + { + "name": "Deadpond", + "age": None, + "team_id": 1, + "id": 1, + "secret_name": "Dive Wilson", + }, + ], + [ + "Created hero:", + { + "name": "Rusty-Man", + "age": 48, + "team_id": 2, + "id": 2, + "secret_name": "Tommy Sharp", + }, + ], + [ + "Created hero:", + { + "name": "Spider-Boy", + "age": None, + "team_id": None, + "id": 3, + "secret_name": "Pedro Parqueador", + }, + ], +] + + +@needs_py39 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.relationship_attributes.define_relationship_attributes import ( + tutorial001_py39 as mod, + ) + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_relationship_attributes/test_read_relationships/test_tutorial001_py310.py b/tests/test_tutorial/test_relationship_attributes/test_read_relationships/test_tutorial001_py310.py new file mode 100644 index 00000000..9a4e3cc5 --- /dev/null +++ b/tests/test_tutorial/test_relationship_attributes/test_read_relationships/test_tutorial001_py310.py @@ -0,0 +1,107 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ....conftest import get_testing_print_function, needs_py310 + +expected_calls = [ + [ + "Created hero:", + { + "age": None, + "id": 1, + "secret_name": "Dive Wilson", + "team_id": 1, + "name": "Deadpond", + }, + ], + [ + "Created hero:", + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 2, + "name": "Rusty-Man", + }, + ], + [ + "Created hero:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": None, + "name": "Spider-Boy", + }, + ], + [ + "Updated hero:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 2, + "name": "Spider-Boy", + }, + ], + [ + "Team Wakaland:", + {"headquarters": "Wakaland Capital City", "id": 3, "name": "Wakaland"}, + ], + [ + "Preventers new hero:", + { + "age": 32, + "id": 6, + "secret_name": "Natalia Roman-on", + "team_id": 2, + "name": "Tarantula", + }, + ], + [ + "Preventers new hero:", + { + "age": 36, + "id": 7, + "secret_name": "Steve Weird", + "team_id": 2, + "name": "Dr. Weird", + }, + ], + [ + "Preventers new hero:", + { + "age": 93, + "id": 8, + "secret_name": "Esteban Rogelios", + "team_id": 2, + "name": "Captain North America", + }, + ], + [ + "Spider-Boy's team:", + {"headquarters": "Sharp Tower", "id": 2, "name": "Preventers"}, + ], + [ + "Spider-Boy's team again:", + {"headquarters": "Sharp Tower", "id": 2, "name": "Preventers"}, + ], +] + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.relationship_attributes.read_relationships import ( + tutorial001_py310 as mod, + ) + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_relationship_attributes/test_read_relationships/test_tutorial001_py39.py b/tests/test_tutorial/test_relationship_attributes/test_read_relationships/test_tutorial001_py39.py new file mode 100644 index 00000000..6b239806 --- /dev/null +++ b/tests/test_tutorial/test_relationship_attributes/test_read_relationships/test_tutorial001_py39.py @@ -0,0 +1,107 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ....conftest import get_testing_print_function, needs_py39 + +expected_calls = [ + [ + "Created hero:", + { + "age": None, + "id": 1, + "secret_name": "Dive Wilson", + "team_id": 1, + "name": "Deadpond", + }, + ], + [ + "Created hero:", + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 2, + "name": "Rusty-Man", + }, + ], + [ + "Created hero:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": None, + "name": "Spider-Boy", + }, + ], + [ + "Updated hero:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 2, + "name": "Spider-Boy", + }, + ], + [ + "Team Wakaland:", + {"headquarters": "Wakaland Capital City", "id": 3, "name": "Wakaland"}, + ], + [ + "Preventers new hero:", + { + "age": 32, + "id": 6, + "secret_name": "Natalia Roman-on", + "team_id": 2, + "name": "Tarantula", + }, + ], + [ + "Preventers new hero:", + { + "age": 36, + "id": 7, + "secret_name": "Steve Weird", + "team_id": 2, + "name": "Dr. Weird", + }, + ], + [ + "Preventers new hero:", + { + "age": 93, + "id": 8, + "secret_name": "Esteban Rogelios", + "team_id": 2, + "name": "Captain North America", + }, + ], + [ + "Spider-Boy's team:", + {"headquarters": "Sharp Tower", "id": 2, "name": "Preventers"}, + ], + [ + "Spider-Boy's team again:", + {"headquarters": "Sharp Tower", "id": 2, "name": "Preventers"}, + ], +] + + +@needs_py39 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.relationship_attributes.read_relationships import ( + tutorial001_py39 as mod, + ) + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_relationship_attributes/test_read_relationships/test_tutorial002_py310.py b/tests/test_tutorial/test_relationship_attributes/test_read_relationships/test_tutorial002_py310.py new file mode 100644 index 00000000..0cc9ae33 --- /dev/null +++ b/tests/test_tutorial/test_relationship_attributes/test_read_relationships/test_tutorial002_py310.py @@ -0,0 +1,149 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ....conftest import get_testing_print_function, needs_py310 + +expected_calls = [ + [ + "Created hero:", + { + "age": None, + "id": 1, + "secret_name": "Dive Wilson", + "team_id": 1, + "name": "Deadpond", + }, + ], + [ + "Created hero:", + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 2, + "name": "Rusty-Man", + }, + ], + [ + "Created hero:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": None, + "name": "Spider-Boy", + }, + ], + [ + "Updated hero:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 2, + "name": "Spider-Boy", + }, + ], + [ + "Team Wakaland:", + {"id": 3, "name": "Wakaland", "headquarters": "Wakaland Capital City"}, + ], + [ + "Preventers new hero:", + { + "age": 32, + "id": 6, + "secret_name": "Natalia Roman-on", + "team_id": 2, + "name": "Tarantula", + }, + ], + [ + "Preventers new hero:", + { + "age": 36, + "id": 7, + "secret_name": "Steve Weird", + "team_id": 2, + "name": "Dr. Weird", + }, + ], + [ + "Preventers new hero:", + { + "age": 93, + "id": 8, + "secret_name": "Esteban Rogelios", + "team_id": 2, + "name": "Captain North America", + }, + ], + [ + "Preventers heroes:", + [ + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 2, + "name": "Rusty-Man", + }, + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 2, + "name": "Spider-Boy", + }, + { + "age": 32, + "id": 6, + "secret_name": "Natalia Roman-on", + "team_id": 2, + "name": "Tarantula", + }, + { + "age": 36, + "id": 7, + "secret_name": "Steve Weird", + "team_id": 2, + "name": "Dr. Weird", + }, + { + "age": 93, + "id": 8, + "secret_name": "Esteban Rogelios", + "team_id": 2, + "name": "Captain North America", + }, + ], + ], + [ + "Spider-Boy without team:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": None, + "name": "Spider-Boy", + }, + ], +] + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.relationship_attributes.read_relationships import ( + tutorial002_py310 as mod, + ) + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_relationship_attributes/test_read_relationships/test_tutorial002_py39.py b/tests/test_tutorial/test_relationship_attributes/test_read_relationships/test_tutorial002_py39.py new file mode 100644 index 00000000..891f4ca6 --- /dev/null +++ b/tests/test_tutorial/test_relationship_attributes/test_read_relationships/test_tutorial002_py39.py @@ -0,0 +1,149 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ....conftest import get_testing_print_function, needs_py39 + +expected_calls = [ + [ + "Created hero:", + { + "age": None, + "id": 1, + "secret_name": "Dive Wilson", + "team_id": 1, + "name": "Deadpond", + }, + ], + [ + "Created hero:", + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 2, + "name": "Rusty-Man", + }, + ], + [ + "Created hero:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": None, + "name": "Spider-Boy", + }, + ], + [ + "Updated hero:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 2, + "name": "Spider-Boy", + }, + ], + [ + "Team Wakaland:", + {"id": 3, "name": "Wakaland", "headquarters": "Wakaland Capital City"}, + ], + [ + "Preventers new hero:", + { + "age": 32, + "id": 6, + "secret_name": "Natalia Roman-on", + "team_id": 2, + "name": "Tarantula", + }, + ], + [ + "Preventers new hero:", + { + "age": 36, + "id": 7, + "secret_name": "Steve Weird", + "team_id": 2, + "name": "Dr. Weird", + }, + ], + [ + "Preventers new hero:", + { + "age": 93, + "id": 8, + "secret_name": "Esteban Rogelios", + "team_id": 2, + "name": "Captain North America", + }, + ], + [ + "Preventers heroes:", + [ + { + "age": 48, + "id": 2, + "secret_name": "Tommy Sharp", + "team_id": 2, + "name": "Rusty-Man", + }, + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": 2, + "name": "Spider-Boy", + }, + { + "age": 32, + "id": 6, + "secret_name": "Natalia Roman-on", + "team_id": 2, + "name": "Tarantula", + }, + { + "age": 36, + "id": 7, + "secret_name": "Steve Weird", + "team_id": 2, + "name": "Dr. Weird", + }, + { + "age": 93, + "id": 8, + "secret_name": "Esteban Rogelios", + "team_id": 2, + "name": "Captain North America", + }, + ], + ], + [ + "Spider-Boy without team:", + { + "age": None, + "id": 3, + "secret_name": "Pedro Parqueador", + "team_id": None, + "name": "Spider-Boy", + }, + ], +] + + +@needs_py39 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.relationship_attributes.read_relationships import ( + tutorial002_py39 as mod, + ) + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_select/test_tutorial001_py310_tutorial002_py310.py b/tests/test_tutorial/test_select/test_tutorial001_py310_tutorial002_py310.py new file mode 100644 index 00000000..7521b6b7 --- /dev/null +++ b/tests/test_tutorial/test_select/test_tutorial001_py310_tutorial002_py310.py @@ -0,0 +1,57 @@ +from typing import Any, Dict, List, Union +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + + +def check_calls(calls: List[List[Union[str, Dict[str, Any]]]]): + assert calls[0][0] == { + "name": "Deadpond", + "secret_name": "Dive Wilson", + "age": None, + "id": 1, + } + assert calls[1][0] == { + "name": "Spider-Boy", + "secret_name": "Pedro Parqueador", + "age": None, + "id": 2, + } + assert calls[2][0] == { + "name": "Rusty-Man", + "secret_name": "Tommy Sharp", + "age": 48, + "id": 3, + } + + +@needs_py310 +def test_tutorial_001(clear_sqlmodel): + from docs_src.tutorial.select import tutorial001_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + check_calls(calls) + + +@needs_py310 +def test_tutorial_002(clear_sqlmodel): + from docs_src.tutorial.select import tutorial002_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + check_calls(calls) diff --git a/tests/test_tutorial/test_select/test_tutorial003_py310_tutorial004_py310.py b/tests/test_tutorial/test_select/test_tutorial003_py310_tutorial004_py310.py new file mode 100644 index 00000000..0fa69df4 --- /dev/null +++ b/tests/test_tutorial/test_select/test_tutorial003_py310_tutorial004_py310.py @@ -0,0 +1,59 @@ +from typing import Any, Dict, List, Union +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + + +def check_calls(calls: List[List[Union[str, Dict[str, Any]]]]): + assert calls[0][0] == [ + { + "name": "Deadpond", + "secret_name": "Dive Wilson", + "age": None, + "id": 1, + }, + { + "name": "Spider-Boy", + "secret_name": "Pedro Parqueador", + "age": None, + "id": 2, + }, + { + "name": "Rusty-Man", + "secret_name": "Tommy Sharp", + "age": 48, + "id": 3, + }, + ] + + +@needs_py310 +def test_tutorial_003(clear_sqlmodel): + from docs_src.tutorial.select import tutorial003_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + check_calls(calls) + + +@needs_py310 +def test_tutorial_002(clear_sqlmodel): + from docs_src.tutorial.select import tutorial004_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + check_calls(calls) diff --git a/tests/test_tutorial/test_update/test_tutorial001_py310_tutorial002_py310.py b/tests/test_tutorial/test_update/test_tutorial001_py310_tutorial002_py310.py new file mode 100644 index 00000000..cefb75f3 --- /dev/null +++ b/tests/test_tutorial/test_update/test_tutorial001_py310_tutorial002_py310.py @@ -0,0 +1,56 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + +expected_calls = [ + [ + "Hero:", + { + "id": 2, + "name": "Spider-Boy", + "secret_name": "Pedro Parqueador", + "age": None, + }, + ], + [ + "Updated hero:", + { + "id": 2, + "name": "Spider-Boy", + "secret_name": "Pedro Parqueador", + "age": 16, + }, + ], +] + + +@needs_py310 +def test_tutorial001(clear_sqlmodel): + from docs_src.tutorial.update import tutorial001_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls + + +@needs_py310 +def test_tutorial002(clear_sqlmodel): + from docs_src.tutorial.update import tutorial002_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_update/test_tutorial003_py310_tutorial004_py310.py b/tests/test_tutorial/test_update/test_tutorial003_py310_tutorial004_py310.py new file mode 100644 index 00000000..31dc6019 --- /dev/null +++ b/tests/test_tutorial/test_update/test_tutorial003_py310_tutorial004_py310.py @@ -0,0 +1,69 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + +expected_calls = [ + [ + "Hero 1:", + {"id": 2, "name": "Spider-Boy", "secret_name": "Pedro Parqueador", "age": None}, + ], + [ + "Hero 2:", + { + "id": 7, + "name": "Captain North America", + "secret_name": "Esteban Rogelios", + "age": 93, + }, + ], + [ + "Updated hero 1:", + { + "id": 2, + "name": "Spider-Youngster", + "secret_name": "Pedro Parqueador", + "age": 16, + }, + ], + [ + "Updated hero 2:", + { + "id": 7, + "name": "Captain North America Except Canada", + "secret_name": "Esteban Rogelios", + "age": 110, + }, + ], +] + + +@needs_py310 +def test_tutorial003(clear_sqlmodel): + from docs_src.tutorial.update import tutorial003_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls + + +@needs_py310 +def test_tutorial004(clear_sqlmodel): + from docs_src.tutorial.update import tutorial004_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == expected_calls diff --git a/tests/test_tutorial/test_where/test_tutorial001_py310.py b/tests/test_tutorial/test_where/test_tutorial001_py310.py new file mode 100644 index 00000000..44e734ad --- /dev/null +++ b/tests/test_tutorial/test_where/test_tutorial001_py310.py @@ -0,0 +1,29 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.where import tutorial001_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == [ + [ + { + "name": "Deadpond", + "secret_name": "Dive Wilson", + "age": None, + "id": 1, + } + ] + ] diff --git a/tests/test_tutorial/test_where/test_tutorial002_py310.py b/tests/test_tutorial/test_where/test_tutorial002_py310.py new file mode 100644 index 00000000..00d88ecd --- /dev/null +++ b/tests/test_tutorial/test_where/test_tutorial002_py310.py @@ -0,0 +1,30 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.where import tutorial002_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == [ + [ + { + "name": "Spider-Boy", + "secret_name": "Pedro Parqueador", + "age": None, + "id": 2, + } + ], + [{"name": "Rusty-Man", "secret_name": "Tommy Sharp", "age": 48, "id": 3}], + ] diff --git a/tests/test_tutorial/test_where/test_tutorial003_py310.py b/tests/test_tutorial/test_where/test_tutorial003_py310.py new file mode 100644 index 00000000..2d84c2ca --- /dev/null +++ b/tests/test_tutorial/test_where/test_tutorial003_py310.py @@ -0,0 +1,37 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.where import tutorial003_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + + expected_calls = [ + [{"id": 6, "name": "Dr. Weird", "secret_name": "Steve Weird", "age": 36}], + [{"id": 3, "name": "Rusty-Man", "secret_name": "Tommy Sharp", "age": 48}], + [ + { + "id": 7, + "name": "Captain North America", + "secret_name": "Esteban Rogelios", + "age": 93, + } + ], + ] + for call in expected_calls: + assert call in calls, "This expected item should be in the list" + # Now that this item was checked, remove it from the list + calls.pop(calls.index(call)) + assert len(calls) == 0, "The list should only have the expected items" diff --git a/tests/test_tutorial/test_where/test_tutorial004_py310.py b/tests/test_tutorial/test_where/test_tutorial004_py310.py new file mode 100644 index 00000000..04566cbb --- /dev/null +++ b/tests/test_tutorial/test_where/test_tutorial004_py310.py @@ -0,0 +1,37 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.where import tutorial004_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + expected_calls = [ + [{"id": 5, "name": "Black Lion", "secret_name": "Trevor Challa", "age": 35}], + [{"id": 6, "name": "Dr. Weird", "secret_name": "Steve Weird", "age": 36}], + [{"id": 3, "name": "Rusty-Man", "secret_name": "Tommy Sharp", "age": 48}], + [ + { + "id": 7, + "name": "Captain North America", + "secret_name": "Esteban Rogelios", + "age": 93, + } + ], + ] + for call in expected_calls: + assert call in calls, "This expected item should be in the list" + # Now that this item was checked, remove it from the list + calls.pop(calls.index(call)) + assert len(calls) == 0, "The list should only have the expected items" diff --git a/tests/test_tutorial/test_where/test_tutorial005_py310.py b/tests/test_tutorial/test_where/test_tutorial005_py310.py new file mode 100644 index 00000000..d238fff4 --- /dev/null +++ b/tests/test_tutorial/test_where/test_tutorial005_py310.py @@ -0,0 +1,22 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.where import tutorial005_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == [ + [{"name": "Tarantula", "secret_name": "Natalia Roman-on", "age": 32, "id": 4}] + ] diff --git a/tests/test_tutorial/test_where/test_tutorial006_py310.py b/tests/test_tutorial/test_where/test_tutorial006_py310.py new file mode 100644 index 00000000..8a4924fc --- /dev/null +++ b/tests/test_tutorial/test_where/test_tutorial006_py310.py @@ -0,0 +1,23 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.where import tutorial006_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == [ + [{"name": "Tarantula", "secret_name": "Natalia Roman-on", "age": 32, "id": 4}], + [{"name": "Black Lion", "secret_name": "Trevor Challa", "age": 35, "id": 5}], + ] diff --git a/tests/test_tutorial/test_where/test_tutorial007_py310.py b/tests/test_tutorial/test_where/test_tutorial007_py310.py new file mode 100644 index 00000000..a2110a19 --- /dev/null +++ b/tests/test_tutorial/test_where/test_tutorial007_py310.py @@ -0,0 +1,23 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.where import tutorial007_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == [ + [{"id": 5, "name": "Black Lion", "secret_name": "Trevor Challa", "age": 35}], + [{"id": 6, "name": "Dr. Weird", "secret_name": "Steve Weird", "age": 36}], + ] diff --git a/tests/test_tutorial/test_where/test_tutorial008_py310.py b/tests/test_tutorial/test_where/test_tutorial008_py310.py new file mode 100644 index 00000000..887ac70a --- /dev/null +++ b/tests/test_tutorial/test_where/test_tutorial008_py310.py @@ -0,0 +1,23 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.where import tutorial008_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == [ + [{"id": 5, "name": "Black Lion", "secret_name": "Trevor Challa", "age": 35}], + [{"id": 6, "name": "Dr. Weird", "secret_name": "Steve Weird", "age": 36}], + ] diff --git a/tests/test_tutorial/test_where/test_tutorial009_py310.py b/tests/test_tutorial/test_where/test_tutorial009_py310.py new file mode 100644 index 00000000..9bbef9b9 --- /dev/null +++ b/tests/test_tutorial/test_where/test_tutorial009_py310.py @@ -0,0 +1,31 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.where import tutorial009_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == [ + [{"name": "Tarantula", "secret_name": "Natalia Roman-on", "age": 32, "id": 4}], + [{"name": "Black Lion", "secret_name": "Trevor Challa", "age": 35, "id": 5}], + [ + { + "name": "Captain North America", + "secret_name": "Esteban Rogelios", + "age": 93, + "id": 7, + } + ], + ] diff --git a/tests/test_tutorial/test_where/test_tutorial010_py310.py b/tests/test_tutorial/test_where/test_tutorial010_py310.py new file mode 100644 index 00000000..e990abed --- /dev/null +++ b/tests/test_tutorial/test_where/test_tutorial010_py310.py @@ -0,0 +1,31 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.where import tutorial010_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == [ + [{"name": "Tarantula", "secret_name": "Natalia Roman-on", "age": 32, "id": 4}], + [{"name": "Black Lion", "secret_name": "Trevor Challa", "age": 35, "id": 5}], + [ + { + "name": "Captain North America", + "secret_name": "Esteban Rogelios", + "age": 93, + "id": 7, + } + ], + ] diff --git a/tests/test_tutorial/test_where/test_tutorial011_py310.py b/tests/test_tutorial/test_where/test_tutorial011_py310.py new file mode 100644 index 00000000..aee809b1 --- /dev/null +++ b/tests/test_tutorial/test_where/test_tutorial011_py310.py @@ -0,0 +1,37 @@ +from unittest.mock import patch + +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function, needs_py310 + + +@needs_py310 +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.where import tutorial011_py310 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + expected_calls = [ + [{"id": 5, "name": "Black Lion", "secret_name": "Trevor Challa", "age": 35}], + [{"id": 6, "name": "Dr. Weird", "secret_name": "Steve Weird", "age": 36}], + [{"id": 3, "name": "Rusty-Man", "secret_name": "Tommy Sharp", "age": 48}], + [ + { + "id": 7, + "name": "Captain North America", + "secret_name": "Esteban Rogelios", + "age": 93, + } + ], + ] + for call in expected_calls: + assert call in calls, "This expected item should be in the list" + # Now that this item was checked, remove it from the list + calls.pop(calls.index(call)) + assert len(calls) == 0, "The list should only have the expected items"