]> git.ipfire.org Git - thirdparty/fastapi/sqlmodel.git/commitdiff
📝 Add source examples for Python 3.9 and 3.10 (#715)
authorSebastián Ramírez <tiangolo@gmail.com>
Wed, 29 Nov 2023 15:51:55 +0000 (16:51 +0100)
committerGitHub <noreply@github.com>
Wed, 29 Nov 2023 15:51:55 +0000 (16:51 +0100)
* 📝 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

243 files changed:
docs_src/advanced/decimal/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/automatic_id_none_refresh/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/automatic_id_none_refresh/tutorial002_py310.py [new file with mode: 0644]
docs_src/tutorial/code_structure/tutorial001_py310/__init__.py [new file with mode: 0644]
docs_src/tutorial/code_structure/tutorial001_py310/app.py [new file with mode: 0644]
docs_src/tutorial/code_structure/tutorial001_py310/database.py [new file with mode: 0644]
docs_src/tutorial/code_structure/tutorial001_py310/models.py [new file with mode: 0644]
docs_src/tutorial/code_structure/tutorial001_py39/__init__.py [new file with mode: 0644]
docs_src/tutorial/code_structure/tutorial001_py39/app.py [new file with mode: 0644]
docs_src/tutorial/code_structure/tutorial001_py39/database.py [new file with mode: 0644]
docs_src/tutorial/code_structure/tutorial001_py39/models.py [new file with mode: 0644]
docs_src/tutorial/code_structure/tutorial002_py310/__init__.py [new file with mode: 0644]
docs_src/tutorial/code_structure/tutorial002_py310/app.py [new file with mode: 0644]
docs_src/tutorial/code_structure/tutorial002_py310/database.py [new file with mode: 0644]
docs_src/tutorial/code_structure/tutorial002_py310/hero_model.py [new file with mode: 0644]
docs_src/tutorial/code_structure/tutorial002_py310/team_model.py [new file with mode: 0644]
docs_src/tutorial/code_structure/tutorial002_py39/__init__.py [new file with mode: 0644]
docs_src/tutorial/code_structure/tutorial002_py39/app.py [new file with mode: 0644]
docs_src/tutorial/code_structure/tutorial002_py39/database.py [new file with mode: 0644]
docs_src/tutorial/code_structure/tutorial002_py39/hero_model.py [new file with mode: 0644]
docs_src/tutorial/code_structure/tutorial002_py39/team_model.py [new file with mode: 0644]
docs_src/tutorial/connect/create_tables/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/connect/delete/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/connect/insert/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/connect/select/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/connect/select/tutorial002_py310.py [new file with mode: 0644]
docs_src/tutorial/connect/select/tutorial003_py310.py [new file with mode: 0644]
docs_src/tutorial/connect/select/tutorial004_py310.py [new file with mode: 0644]
docs_src/tutorial/connect/select/tutorial005_py310.py [new file with mode: 0644]
docs_src/tutorial/connect/update/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/create_db_and_table/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/create_db_and_table/tutorial002_py310.py [new file with mode: 0644]
docs_src/tutorial/create_db_and_table/tutorial003_py310.py [new file with mode: 0644]
docs_src/tutorial/delete/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/delete/tutorial002_py310.py [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001/test_extra_coverage.py [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py310/__init__.py [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py310/annotations/en/test_main_001.md [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py310/annotations/en/test_main_002.md [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py310/annotations/en/test_main_003.md [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py310/annotations/en/test_main_004.md [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py310/annotations/en/test_main_005.md [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py310/annotations/en/test_main_006.md [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py310/main.py [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_extra_coverage.py [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_main.py [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_main_001.py [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_main_002.py [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_main_003.py [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_main_004.py [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_main_005.py [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_main_006.py [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py39/__init__.py [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py39/annotations/en/test_main_001.md [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py39/annotations/en/test_main_002.md [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py39/annotations/en/test_main_003.md [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py39/annotations/en/test_main_004.md [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py39/annotations/en/test_main_005.md [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py39/annotations/en/test_main_006.md [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py39/main.py [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_extra_coverage.py [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_main.py [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_main_001.py [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_main_002.py [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_main_003.py [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_main_004.py [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_main_005.py [new file with mode: 0644]
docs_src/tutorial/fastapi/app_testing/tutorial001_py39/test_main_006.py [new file with mode: 0644]
docs_src/tutorial/fastapi/delete/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/fastapi/delete/tutorial001_py39.py [new file with mode: 0644]
docs_src/tutorial/fastapi/limit_and_offset/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/fastapi/limit_and_offset/tutorial001_py39.py [new file with mode: 0644]
docs_src/tutorial/fastapi/multiple_models/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/fastapi/multiple_models/tutorial001_py39.py [new file with mode: 0644]
docs_src/tutorial/fastapi/multiple_models/tutorial002_py310.py [new file with mode: 0644]
docs_src/tutorial/fastapi/multiple_models/tutorial002_py39.py [new file with mode: 0644]
docs_src/tutorial/fastapi/read_one/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/fastapi/read_one/tutorial001_py39.py [new file with mode: 0644]
docs_src/tutorial/fastapi/relationships/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/fastapi/relationships/tutorial001_py39.py [new file with mode: 0644]
docs_src/tutorial/fastapi/response_model/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/fastapi/response_model/tutorial001_py39.py [new file with mode: 0644]
docs_src/tutorial/fastapi/session_with_dependency/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/fastapi/session_with_dependency/tutorial001_py39.py [new file with mode: 0644]
docs_src/tutorial/fastapi/simple_hero_api/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/fastapi/teams/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/fastapi/teams/tutorial001_py39.py [new file with mode: 0644]
docs_src/tutorial/fastapi/update/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/fastapi/update/tutorial001_py39.py [new file with mode: 0644]
docs_src/tutorial/indexes/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/indexes/tutorial002_py310.py [new file with mode: 0644]
docs_src/tutorial/insert/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/insert/tutorial002_py310.py [new file with mode: 0644]
docs_src/tutorial/insert/tutorial003_py310.py [new file with mode: 0644]
docs_src/tutorial/many_to_many/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/many_to_many/tutorial001_py39.py [new file with mode: 0644]
docs_src/tutorial/many_to_many/tutorial002_py310.py [new file with mode: 0644]
docs_src/tutorial/many_to_many/tutorial002_py39.py [new file with mode: 0644]
docs_src/tutorial/many_to_many/tutorial003_py310.py [new file with mode: 0644]
docs_src/tutorial/many_to_many/tutorial003_py39.py [new file with mode: 0644]
docs_src/tutorial/offset_and_limit/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/offset_and_limit/tutorial002_py310.py [new file with mode: 0644]
docs_src/tutorial/offset_and_limit/tutorial003_py310.py [new file with mode: 0644]
docs_src/tutorial/offset_and_limit/tutorial004_py310.py [new file with mode: 0644]
docs_src/tutorial/one/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/one/tutorial002_py310.py [new file with mode: 0644]
docs_src/tutorial/one/tutorial003_py310.py [new file with mode: 0644]
docs_src/tutorial/one/tutorial004_py310.py [new file with mode: 0644]
docs_src/tutorial/one/tutorial005_py310.py [new file with mode: 0644]
docs_src/tutorial/one/tutorial006_py310.py [new file with mode: 0644]
docs_src/tutorial/one/tutorial007_py310.py [new file with mode: 0644]
docs_src/tutorial/one/tutorial008_py310.py [new file with mode: 0644]
docs_src/tutorial/one/tutorial009_py310.py [new file with mode: 0644]
docs_src/tutorial/relationship_attributes/back_populates/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/relationship_attributes/back_populates/tutorial001_py39.py [new file with mode: 0644]
docs_src/tutorial/relationship_attributes/back_populates/tutorial002_py310.py [new file with mode: 0644]
docs_src/tutorial/relationship_attributes/back_populates/tutorial002_py39.py [new file with mode: 0644]
docs_src/tutorial/relationship_attributes/back_populates/tutorial003_py310.py [new file with mode: 0644]
docs_src/tutorial/relationship_attributes/back_populates/tutorial003_py39.py [new file with mode: 0644]
docs_src/tutorial/relationship_attributes/create_and_update_relationships/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/relationship_attributes/create_and_update_relationships/tutorial001_py39.py [new file with mode: 0644]
docs_src/tutorial/relationship_attributes/define_relationship_attributes/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/relationship_attributes/define_relationship_attributes/tutorial001_py39.py [new file with mode: 0644]
docs_src/tutorial/relationship_attributes/read_relationships/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/relationship_attributes/read_relationships/tutorial001_py39.py [new file with mode: 0644]
docs_src/tutorial/relationship_attributes/read_relationships/tutorial002_py310.py [new file with mode: 0644]
docs_src/tutorial/relationship_attributes/read_relationships/tutorial002_py39.py [new file with mode: 0644]
docs_src/tutorial/select/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/select/tutorial002_py310.py [new file with mode: 0644]
docs_src/tutorial/select/tutorial003_py310.py [new file with mode: 0644]
docs_src/tutorial/select/tutorial004_py310.py [new file with mode: 0644]
docs_src/tutorial/update/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/update/tutorial002_py310.py [new file with mode: 0644]
docs_src/tutorial/update/tutorial003_py310.py [new file with mode: 0644]
docs_src/tutorial/update/tutorial004_py310.py [new file with mode: 0644]
docs_src/tutorial/where/tutorial001_py310.py [new file with mode: 0644]
docs_src/tutorial/where/tutorial002_py310.py [new file with mode: 0644]
docs_src/tutorial/where/tutorial003_py310.py [new file with mode: 0644]
docs_src/tutorial/where/tutorial004_py310.py [new file with mode: 0644]
docs_src/tutorial/where/tutorial005_py310.py [new file with mode: 0644]
docs_src/tutorial/where/tutorial006_py310.py [new file with mode: 0644]
docs_src/tutorial/where/tutorial007_py310.py [new file with mode: 0644]
docs_src/tutorial/where/tutorial008_py310.py [new file with mode: 0644]
docs_src/tutorial/where/tutorial009_py310.py [new file with mode: 0644]
docs_src/tutorial/where/tutorial010_py310.py [new file with mode: 0644]
docs_src/tutorial/where/tutorial011_py310.py [new file with mode: 0644]
tests/conftest.py
tests/test_advanced/test_decimal/test_tutorial001_py310.py [new file with mode: 0644]
tests/test_tutorial/test_automatic_id_none_refresh/test_tutorial001_py310_tutorial002_py310.py [new file with mode: 0644]
tests/test_tutorial/test_code_structure/test_tutorial001_py310.py [new file with mode: 0644]
tests/test_tutorial/test_code_structure/test_tutorial001_py39.py [new file with mode: 0644]
tests/test_tutorial/test_code_structure/test_tutorial002_py310.py [new file with mode: 0644]
tests/test_tutorial/test_code_structure/test_tutorial002_py39.py [new file with mode: 0644]
tests/test_tutorial/test_connect/test_create_connected_tables/test_tutorial001_py310.py [new file with mode: 0644]
tests/test_tutorial/test_connect/test_delete/test_tutorial001_py310.py [new file with mode: 0644]
tests/test_tutorial/test_connect/test_insert/test_tutorial001_py310.py [new file with mode: 0644]
tests/test_tutorial/test_connect/test_select/test_tutorial001_py310_tutorial002_py310.py [new file with mode: 0644]
tests/test_tutorial/test_connect/test_select/test_tutorial003_py310.py [new file with mode: 0644]
tests/test_tutorial/test_connect/test_select/test_tutorial004_py310.py [new file with mode: 0644]
tests/test_tutorial/test_connect/test_select/test_tutorial005_py310.py [new file with mode: 0644]
tests/test_tutorial/test_connect/test_update/test_tutorial001_py310.py [new file with mode: 0644]
tests/test_tutorial/test_create_db_and_table/test_tutorial001_py310.py [new file with mode: 0644]
tests/test_tutorial/test_create_db_and_table/test_tutorial002_py310.py [new file with mode: 0644]
tests/test_tutorial/test_create_db_and_table/test_tutorial003_py310.py [new file with mode: 0644]
tests/test_tutorial/test_delete/test_tutorial001_py310_tutorial002_py310.py [new file with mode: 0644]
tests/test_tutorial/test_fastapi/test_app_testing/test_tutorial001_py310_tests_main.py [new file with mode: 0644]
tests/test_tutorial/test_fastapi/test_app_testing/test_tutorial001_py39_tests_main.py [new file with mode: 0644]
tests/test_tutorial/test_fastapi/test_app_testing/test_tutorial001_tests_main.py
tests/test_tutorial/test_fastapi/test_delete/test_tutorial001_py310.py [new file with mode: 0644]
tests/test_tutorial/test_fastapi/test_delete/test_tutorial001_py39.py [new file with mode: 0644]
tests/test_tutorial/test_fastapi/test_limit_and_offset/test_tutorial001_py310.py [new file with mode: 0644]
tests/test_tutorial/test_fastapi/test_limit_and_offset/test_tutorial001_py39.py [new file with mode: 0644]
tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial001_py310.py [new file with mode: 0644]
tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial001_py39.py [new file with mode: 0644]
tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial002_py310.py [new file with mode: 0644]
tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial002_py39.py [new file with mode: 0644]
tests/test_tutorial/test_fastapi/test_read_one/test_tutorial001_py310.py [new file with mode: 0644]
tests/test_tutorial/test_fastapi/test_read_one/test_tutorial001_py39.py [new file with mode: 0644]
tests/test_tutorial/test_fastapi/test_relationships/test_tutorial001_py310.py [new file with mode: 0644]
tests/test_tutorial/test_fastapi/test_relationships/test_tutorial001_py39.py [new file with mode: 0644]
tests/test_tutorial/test_fastapi/test_response_model/test_tutorial001_py310.py [new file with mode: 0644]
tests/test_tutorial/test_fastapi/test_response_model/test_tutorial001_py39.py [new file with mode: 0644]
tests/test_tutorial/test_fastapi/test_session_with_dependency/test_tutorial001_py310.py [new file with mode: 0644]
tests/test_tutorial/test_fastapi/test_session_with_dependency/test_tutorial001_py39.py [new file with mode: 0644]
tests/test_tutorial/test_fastapi/test_simple_hero_api/test_tutorial001_py310.py [new file with mode: 0644]
tests/test_tutorial/test_fastapi/test_teams/test_tutorial001_py310.py [new file with mode: 0644]
tests/test_tutorial/test_fastapi/test_teams/test_tutorial001_py39.py [new file with mode: 0644]
tests/test_tutorial/test_fastapi/test_update/test_tutorial001_py310.py [new file with mode: 0644]
tests/test_tutorial/test_fastapi/test_update/test_tutorial001_py39.py [new file with mode: 0644]
tests/test_tutorial/test_indexes/test_tutorial001_py310.py [new file with mode: 0644]
tests/test_tutorial/test_indexes/test_tutorial002.py [moved from tests/test_tutorial/test_indexes/test_tutorial006.py with 100% similarity]
tests/test_tutorial/test_indexes/test_tutorial002_py310.py [new file with mode: 0644]
tests/test_tutorial/test_insert/test_tutorial001_py310.py [new file with mode: 0644]
tests/test_tutorial/test_insert/test_tutorial002_py310.py [new file with mode: 0644]
tests/test_tutorial/test_insert/test_tutorial003_py310.py [new file with mode: 0644]
tests/test_tutorial/test_limit_and_offset/test_tutorial001_py310.py [new file with mode: 0644]
tests/test_tutorial/test_limit_and_offset/test_tutorial002_py310.py [new file with mode: 0644]
tests/test_tutorial/test_limit_and_offset/test_tutorial003_py310.py [new file with mode: 0644]
tests/test_tutorial/test_limit_and_offset/test_tutorial004_py310.py [new file with mode: 0644]
tests/test_tutorial/test_many_to_many/test_tutorial001_py310.py [new file with mode: 0644]
tests/test_tutorial/test_many_to_many/test_tutorial001_py39.py [new file with mode: 0644]
tests/test_tutorial/test_many_to_many/test_tutorial002_py310.py [new file with mode: 0644]
tests/test_tutorial/test_many_to_many/test_tutorial002_py39.py [new file with mode: 0644]
tests/test_tutorial/test_many_to_many/test_tutorial003_py310.py [new file with mode: 0644]
tests/test_tutorial/test_many_to_many/test_tutorial003_py39.py [new file with mode: 0644]
tests/test_tutorial/test_one/test_tutorial001_py310.py [new file with mode: 0644]
tests/test_tutorial/test_one/test_tutorial002_py310.py [new file with mode: 0644]
tests/test_tutorial/test_one/test_tutorial003_py310.py [new file with mode: 0644]
tests/test_tutorial/test_one/test_tutorial004_py310.py [new file with mode: 0644]
tests/test_tutorial/test_one/test_tutorial005_py310.py [new file with mode: 0644]
tests/test_tutorial/test_one/test_tutorial006_py310.py [new file with mode: 0644]
tests/test_tutorial/test_one/test_tutorial007_py310.py [new file with mode: 0644]
tests/test_tutorial/test_one/test_tutorial008_py310.py [new file with mode: 0644]
tests/test_tutorial/test_one/test_tutorial009_py310.py [new file with mode: 0644]
tests/test_tutorial/test_relationship_attributes/test_back_populates/test_tutorial001_py310.py [new file with mode: 0644]
tests/test_tutorial/test_relationship_attributes/test_back_populates/test_tutorial001_py39.py [new file with mode: 0644]
tests/test_tutorial/test_relationship_attributes/test_back_populates/test_tutorial002_py310.py [new file with mode: 0644]
tests/test_tutorial/test_relationship_attributes/test_back_populates/test_tutorial002_py39.py [new file with mode: 0644]
tests/test_tutorial/test_relationship_attributes/test_back_populates/test_tutorial003_py310.py [new file with mode: 0644]
tests/test_tutorial/test_relationship_attributes/test_back_populates/test_tutorial003_py39.py [new file with mode: 0644]
tests/test_tutorial/test_relationship_attributes/test_create_and_update_relationships/test_tutorial001_py310.py [new file with mode: 0644]
tests/test_tutorial/test_relationship_attributes/test_create_and_update_relationships/test_tutorial001_py39.py [new file with mode: 0644]
tests/test_tutorial/test_relationship_attributes/test_define_relationship_attributes/test_tutorial001_py310.py [new file with mode: 0644]
tests/test_tutorial/test_relationship_attributes/test_define_relationship_attributes/test_tutorial001_py39.py [new file with mode: 0644]
tests/test_tutorial/test_relationship_attributes/test_read_relationships/test_tutorial001_py310.py [new file with mode: 0644]
tests/test_tutorial/test_relationship_attributes/test_read_relationships/test_tutorial001_py39.py [new file with mode: 0644]
tests/test_tutorial/test_relationship_attributes/test_read_relationships/test_tutorial002_py310.py [new file with mode: 0644]
tests/test_tutorial/test_relationship_attributes/test_read_relationships/test_tutorial002_py39.py [new file with mode: 0644]
tests/test_tutorial/test_select/test_tutorial001_py310_tutorial002_py310.py [new file with mode: 0644]
tests/test_tutorial/test_select/test_tutorial003_py310_tutorial004_py310.py [new file with mode: 0644]
tests/test_tutorial/test_update/test_tutorial001_py310_tutorial002_py310.py [new file with mode: 0644]
tests/test_tutorial/test_update/test_tutorial003_py310_tutorial004_py310.py [new file with mode: 0644]
tests/test_tutorial/test_where/test_tutorial001_py310.py [new file with mode: 0644]
tests/test_tutorial/test_where/test_tutorial002_py310.py [new file with mode: 0644]
tests/test_tutorial/test_where/test_tutorial003_py310.py [new file with mode: 0644]
tests/test_tutorial/test_where/test_tutorial004_py310.py [new file with mode: 0644]
tests/test_tutorial/test_where/test_tutorial005_py310.py [new file with mode: 0644]
tests/test_tutorial/test_where/test_tutorial006_py310.py [new file with mode: 0644]
tests/test_tutorial/test_where/test_tutorial007_py310.py [new file with mode: 0644]
tests/test_tutorial/test_where/test_tutorial008_py310.py [new file with mode: 0644]
tests/test_tutorial/test_where/test_tutorial009_py310.py [new file with mode: 0644]
tests/test_tutorial/test_where/test_tutorial010_py310.py [new file with mode: 0644]
tests/test_tutorial/test_where/test_tutorial011_py310.py [new file with mode: 0644]

diff --git a/docs_src/advanced/decimal/tutorial001_py310.py b/docs_src/advanced/decimal/tutorial001_py310.py
new file mode 100644 (file)
index 0000000..92afc09
--- /dev/null
@@ -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 (file)
index 0000000..6b76da5
--- /dev/null
@@ -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 (file)
index 0000000..0f3ad44
--- /dev/null
@@ -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 (file)
index 0000000..e69de29
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 (file)
index 0000000..065f8a7
--- /dev/null
@@ -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 (file)
index 0000000..d6de16c
--- /dev/null
@@ -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 (file)
index 0000000..1f485ef
--- /dev/null
@@ -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 (file)
index 0000000..e69de29
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 (file)
index 0000000..065f8a7
--- /dev/null
@@ -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 (file)
index 0000000..d6de16c
--- /dev/null
@@ -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 (file)
index 0000000..ff6b6c2
--- /dev/null
@@ -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 (file)
index 0000000..e69de29
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 (file)
index 0000000..8afaee7
--- /dev/null
@@ -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 (file)
index 0000000..d6de16c
--- /dev/null
@@ -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 (file)
index 0000000..52fe68b
--- /dev/null
@@ -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 (file)
index 0000000..10af5b9
--- /dev/null
@@ -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 (file)
index 0000000..e69de29
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 (file)
index 0000000..8afaee7
--- /dev/null
@@ -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 (file)
index 0000000..d6de16c
--- /dev/null
@@ -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 (file)
index 0000000..06dd9c6
--- /dev/null
@@ -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 (file)
index 0000000..b51c070
--- /dev/null
@@ -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 (file)
index 0000000..460b976
--- /dev/null
@@ -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 (file)
index 0000000..4815ad4
--- /dev/null
@@ -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 (file)
index 0000000..506429d
--- /dev/null
@@ -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 (file)
index 0000000..c39894f
--- /dev/null
@@ -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 (file)
index 0000000..756dab0
--- /dev/null
@@ -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 (file)
index 0000000..63f5b27
--- /dev/null
@@ -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 (file)
index 0000000..8b02432
--- /dev/null
@@ -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 (file)
index 0000000..2120640
--- /dev/null
@@ -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 (file)
index 0000000..1fbb108
--- /dev/null
@@ -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 (file)
index 0000000..13ae331
--- /dev/null
@@ -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 (file)
index 0000000..50c7241
--- /dev/null
@@ -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 (file)
index 0000000..01fcb14
--- /dev/null
@@ -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 (file)
index 0000000..7c29efa
--- /dev/null
@@ -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 (file)
index 0000000..afe9a47
--- /dev/null
@@ -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 (file)
index 0000000..1d8153a
--- /dev/null
@@ -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 (file)
index 0000000..e69de29
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 (file)
index 0000000..936b84b
--- /dev/null
@@ -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 (file)
index 0000000..0f8555a
--- /dev/null
@@ -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 (file)
index 0000000..2b48ebd
--- /dev/null
@@ -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 (file)
index 0000000..92cbe77
--- /dev/null
@@ -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 <a href="https://docs.sqlalchemy.org/en/14/dialects/sqlite.html#using-a-memory-database-in-multiple-threads" class="external-link" target="_blank">SQLAlchemy documentation about Using a Memory Database in Multiple Threads</a>
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 (file)
index 0000000..126e1f1
--- /dev/null
@@ -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 (file)
index 0000000..d44a3b6
--- /dev/null
@@ -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 (file)
index 0000000..e8615d9
--- /dev/null
@@ -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 (file)
index 0000000..1d8153a
--- /dev/null
@@ -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 (file)
index 0000000..435787c
--- /dev/null
@@ -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 (file)
index 0000000..3ae4077
--- /dev/null
@@ -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 (file)
index 0000000..727580b
--- /dev/null
@@ -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 (file)
index 0000000..465c525
--- /dev/null
@@ -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 (file)
index 0000000..b770a9a
--- /dev/null
@@ -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 (file)
index 0000000..f653eef
--- /dev/null
@@ -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 (file)
index 0000000..8dbfd45
--- /dev/null
@@ -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 (file)
index 0000000..e69de29
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 (file)
index 0000000..936b84b
--- /dev/null
@@ -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 (file)
index 0000000..0f8555a
--- /dev/null
@@ -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 (file)
index 0000000..2b48ebd
--- /dev/null
@@ -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 (file)
index 0000000..92cbe77
--- /dev/null
@@ -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 <a href="https://docs.sqlalchemy.org/en/14/dialects/sqlite.html#using-a-memory-database-in-multiple-threads" class="external-link" target="_blank">SQLAlchemy documentation about Using a Memory Database in Multiple Threads</a>
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 (file)
index 0000000..126e1f1
--- /dev/null
@@ -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 (file)
index 0000000..d44a3b6
--- /dev/null
@@ -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 (file)
index 0000000..9816e70
--- /dev/null
@@ -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 (file)
index 0000000..1d8153a
--- /dev/null
@@ -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 (file)
index 0000000..435787c
--- /dev/null
@@ -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 (file)
index 0000000..3ae4077
--- /dev/null
@@ -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 (file)
index 0000000..727580b
--- /dev/null
@@ -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 (file)
index 0000000..465c525
--- /dev/null
@@ -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 (file)
index 0000000..b770a9a
--- /dev/null
@@ -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 (file)
index 0000000..f653eef
--- /dev/null
@@ -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 (file)
index 0000000..8dbfd45
--- /dev/null
@@ -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 (file)
index 0000000..5b2da0a
--- /dev/null
@@ -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 (file)
index 0000000..5f498cf
--- /dev/null
@@ -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 (file)
index 0000000..874a6e8
--- /dev/null
@@ -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 (file)
index 0000000..b63fa75
--- /dev/null
@@ -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 (file)
index 0000000..13129f3
--- /dev/null
@@ -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 (file)
index 0000000..41a51f4
--- /dev/null
@@ -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 (file)
index 0000000..3eda88b
--- /dev/null
@@ -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 (file)
index 0000000..473fe5b
--- /dev/null
@@ -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 (file)
index 0000000..8883570
--- /dev/null
@@ -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 (file)
index 0000000..0ad7016
--- /dev/null
@@ -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 (file)
index 0000000..bec6a6f
--- /dev/null
@@ -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 (file)
index 0000000..3893905
--- /dev/null
@@ -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 (file)
index 0000000..25825b4
--- /dev/null
@@ -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 (file)
index 0000000..53b701d
--- /dev/null
@@ -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 (file)
index 0000000..e8615d9
--- /dev/null
@@ -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 (file)
index 0000000..9816e70
--- /dev/null
@@ -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 (file)
index 0000000..0e113b0
--- /dev/null
@@ -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 (file)
index 0000000..a9a527d
--- /dev/null
@@ -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 (file)
index 0000000..1a36428
--- /dev/null
@@ -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 (file)
index 0000000..7906918
--- /dev/null
@@ -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 (file)
index 0000000..c788eb1
--- /dev/null
@@ -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 (file)
index 0000000..115c447
--- /dev/null
@@ -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 (file)
index 0000000..c0b7a1e
--- /dev/null
@@ -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 (file)
index 0000000..72f95ee
--- /dev/null
@@ -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 (file)
index 0000000..266fbb8
--- /dev/null
@@ -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 (file)
index 0000000..da94841
--- /dev/null
@@ -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 (file)
index 0000000..5e8f31c
--- /dev/null
@@ -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 (file)
index 0000000..0d7325a
--- /dev/null
@@ -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 (file)
index 0000000..5823a6e
--- /dev/null
@@ -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 (file)
index 0000000..54c5d45
--- /dev/null
@@ -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 (file)
index 0000000..b8fa463
--- /dev/null
@@ -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 (file)
index 0000000..214228a
--- /dev/null
@@ -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 (file)
index 0000000..931f46e
--- /dev/null
@@ -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 (file)
index 0000000..ab5c89b
--- /dev/null
@@ -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 (file)
index 0000000..5ac2493
--- /dev/null
@@ -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 (file)
index 0000000..c7e7bbb
--- /dev/null
@@ -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 (file)
index 0000000..bb1326d
--- /dev/null
@@ -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 (file)
index 0000000..b82fcfd
--- /dev/null
@@ -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 (file)
index 0000000..f674c8a
--- /dev/null
@@ -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 (file)
index 0000000..e55b553
--- /dev/null
@@ -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 (file)
index 0000000..6c51d8f
--- /dev/null
@@ -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 (file)
index 0000000..6f9b737
--- /dev/null
@@ -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 (file)
index 0000000..f065f5a
--- /dev/null
@@ -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 (file)
index 0000000..af91697
--- /dev/null
@@ -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 (file)
index 0000000..57db99e
--- /dev/null
@@ -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 (file)
index 0000000..fb35500
--- /dev/null
@@ -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 (file)
index 0000000..a5deae9
--- /dev/null
@@ -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 (file)
index 0000000..2113fde
--- /dev/null
@@ -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 (file)
index 0000000..72a8616
--- /dev/null
@@ -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 (file)
index 0000000..aa850b3
--- /dev/null
@@ -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 (file)
index 0000000..fc0d08d
--- /dev/null
@@ -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 (file)
index 0000000..4e03147
--- /dev/null
@@ -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 (file)
index 0000000..bc51058
--- /dev/null
@@ -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 (file)
index 0000000..dc4e261
--- /dev/null
@@ -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 (file)
index 0000000..42ba84d
--- /dev/null
@@ -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 (file)
index 0000000..18b3f17
--- /dev/null
@@ -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 (file)
index 0000000..18c4c2e
--- /dev/null
@@ -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 (file)
index 0000000..f132002
--- /dev/null
@@ -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 (file)
index 0000000..33310e7
--- /dev/null
@@ -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 (file)
index 0000000..29bce28
--- /dev/null
@@ -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 (file)
index 0000000..b2f9d4d
--- /dev/null
@@ -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 (file)
index 0000000..836998e
--- /dev/null
@@ -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 (file)
index 0000000..6366d40
--- /dev/null
@@ -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 (file)
index 0000000..6cc4f48
--- /dev/null
@@ -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 (file)
index 0000000..64cb691
--- /dev/null
@@ -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 (file)
index 0000000..f250b07
--- /dev/null
@@ -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 (file)
index 0000000..09e54e1
--- /dev/null
@@ -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 (file)
index 0000000..a59e5fc
--- /dev/null
@@ -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 (file)
index 0000000..5db10c5
--- /dev/null
@@ -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 (file)
index 0000000..c368add
--- /dev/null
@@ -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 (file)
index 0000000..5733b71
--- /dev/null
@@ -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 (file)
index 0000000..5251506
--- /dev/null
@@ -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 (file)
index 0000000..a3ab850
--- /dev/null
@@ -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 (file)
index 0000000..589bc98
--- /dev/null
@@ -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 (file)
index 0000000..f32260c
--- /dev/null
@@ -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 (file)
index 0000000..0681d1c
--- /dev/null
@@ -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 (file)
index 0000000..a65c47a
--- /dev/null
@@ -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 (file)
index 0000000..73aa4aa
--- /dev/null
@@ -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()
index 2b8e5fc29e1c8a92371fd11ddc610a3131bd213b..7b2cfcd6d54a6efbb880d6be065d6b061a8f977f 100644 (file)
@@ -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 (file)
index 0000000..f58ea11
--- /dev/null
@@ -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 (file)
index 0000000..9ffcd8a
--- /dev/null
@@ -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 (file)
index 0000000..3196560
--- /dev/null
@@ -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 (file)
index 0000000..b101dea
--- /dev/null
@@ -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 (file)
index 0000000..5744aa9
--- /dev/null
@@ -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 (file)
index 0000000..bae15c3
--- /dev/null
@@ -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 (file)
index 0000000..ec2990e
--- /dev/null
@@ -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 (file)
index 0000000..edc70b8
--- /dev/null
@@ -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 (file)
index 0000000..854c006
--- /dev/null
@@ -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 (file)
index 0000000..01762f3
--- /dev/null
@@ -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 (file)
index 0000000..69abccb
--- /dev/null
@@ -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 (file)
index 0000000..72974ec
--- /dev/null
@@ -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 (file)
index 0000000..a7332c1
--- /dev/null
@@ -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 (file)
index 0000000..f370265
--- /dev/null
@@ -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 (file)
index 0000000..465b9f9
--- /dev/null
@@ -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 (file)
index 0000000..3ca3186
--- /dev/null
@@ -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 (file)
index 0000000..a1806ce
--- /dev/null
@@ -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 (file)
index 0000000..0f97e74
--- /dev/null
@@ -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 (file)
index 0000000..781de7c
--- /dev/null
@@ -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 (file)
index 0000000..6dbcc80
--- /dev/null
@@ -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")
index 8051c7ac64f0429692e0a853f86ce742d4b9ed06..d7c1329b386daaa5bc5097ff7902038aeca619b0 100644 (file)
@@ -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 (file)
index 0000000..133b287
--- /dev/null
@@ -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 (file)
index 0000000..5aac8cb
--- /dev/null
@@ -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 (file)
index 0000000..ee0d89a
--- /dev/null
@@ -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 (file)
index 0000000..f4ef44a
--- /dev/null
@@ -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 (file)
index 0000000..080a907
--- /dev/null
@@ -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 (file)
index 0000000..7c32009
--- /dev/null
@@ -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 (file)
index 0000000..20195c6
--- /dev/null
@@ -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 (file)
index 0000000..45b061b
--- /dev/null
@@ -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 (file)
index 0000000..2e0a97e
--- /dev/null
@@ -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 (file)
index 0000000..a663ecc
--- /dev/null
@@ -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 (file)
index 0000000..dae7db3
--- /dev/null
@@ -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 (file)
index 0000000..72dee33
--- /dev/null
@@ -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 (file)
index 0000000..4acb006
--- /dev/null
@@ -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 (file)
index 0000000..20f3f52
--- /dev/null
@@ -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 (file)
index 0000000..f0c5416
--- /dev/null
@@ -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 (file)
index 0000000..5b911c8
--- /dev/null
@@ -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 (file)
index 0000000..d85d9ee
--- /dev/null
@@ -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 (file)
index 0000000..6cec87a
--- /dev/null
@@ -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 (file)
index 0000000..70279f5
--- /dev/null
@@ -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 (file)
index 0000000..cf56e3c
--- /dev/null
@@ -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 (file)
index 0000000..b301ca3
--- /dev/null
@@ -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 (file)
index 0000000..cfee262
--- /dev/null
@@ -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_tutorial002_py310.py b/tests/test_tutorial/test_indexes/test_tutorial002_py310.py
new file mode 100644 (file)
index 0000000..089b682
--- /dev/null
@@ -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 (file)
index 0000000..47cbc4c
--- /dev/null
@@ -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 (file)
index 0000000..fb62810
--- /dev/null
@@ -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 (file)
index 0000000..5bca713
--- /dev/null
@@ -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 (file)
index 0000000..4f4974c
--- /dev/null
@@ -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 (file)
index 0000000..1f86d19
--- /dev/null
@@ -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 (file)
index 0000000..9939991
--- /dev/null
@@ -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 (file)
index 0000000..4ca7365
--- /dev/null
@@ -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 (file)
index 0000000..19a1640
--- /dev/null
@@ -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 (file)
index 0000000..23a7649
--- /dev/null
@@ -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 (file)
index 0000000..d480190
--- /dev/null
@@ -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 (file)
index 0000000..85f649a
--- /dev/null
@@ -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 (file)
index 0000000..29918b3
--- /dev/null
@@ -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 (file)
index 0000000..5f1c87d
--- /dev/null
@@ -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 (file)
index 0000000..6de8780
--- /dev/null
@@ -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 (file)
index 0000000..afdfc54
--- /dev/null
@@ -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 (file)
index 0000000..8eb8b86
--- /dev/null
@@ -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 (file)
index 0000000..cf365a4
--- /dev/null
@@ -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 (file)
index 0000000..f1fce7d
--- /dev/null
@@ -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 (file)
index 0000000..ad8577c
--- /dev/null
@@ -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 (file)
index 0000000..15b2306
--- /dev/null
@@ -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 (file)
index 0000000..c7d1fe5
--- /dev/null
@@ -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 (file)
index 0000000..8e9fda5
--- /dev/null
@@ -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 (file)
index 0000000..384056a
--- /dev/null
@@ -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 (file)
index 0000000..0597a88
--- /dev/null
@@ -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 (file)
index 0000000..50a891f
--- /dev/null
@@ -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 (file)
index 0000000..3da6ce4
--- /dev/null
@@ -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 (file)
index 0000000..82e0c1c
--- /dev/null
@@ -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 (file)
index 0000000..d6059cb
--- /dev/null
@@ -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 (file)
index 0000000..c239b6d
--- /dev/null
@@ -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 (file)
index 0000000..c569eed
--- /dev/null
@@ -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 (file)
index 0000000..f595dca
--- /dev/null
@@ -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 (file)
index 0000000..d54c610
--- /dev/null
@@ -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 (file)
index 0000000..9a4e3cc
--- /dev/null
@@ -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 (file)
index 0000000..6b23980
--- /dev/null
@@ -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 (file)
index 0000000..0cc9ae3
--- /dev/null
@@ -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 (file)
index 0000000..891f4ca
--- /dev/null
@@ -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 (file)
index 0000000..7521b6b
--- /dev/null
@@ -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 (file)
index 0000000..0fa69df
--- /dev/null
@@ -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 (file)
index 0000000..cefb75f
--- /dev/null
@@ -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 (file)
index 0000000..31dc601
--- /dev/null
@@ -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 (file)
index 0000000..44e734a
--- /dev/null
@@ -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 (file)
index 0000000..00d88ec
--- /dev/null
@@ -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 (file)
index 0000000..2d84c2c
--- /dev/null
@@ -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 (file)
index 0000000..04566cb
--- /dev/null
@@ -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 (file)
index 0000000..d238fff
--- /dev/null
@@ -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 (file)
index 0000000..8a4924f
--- /dev/null
@@ -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 (file)
index 0000000..a2110a1
--- /dev/null
@@ -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 (file)
index 0000000..887ac70
--- /dev/null
@@ -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 (file)
index 0000000..9bbef9b
--- /dev/null
@@ -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 (file)
index 0000000..e990abe
--- /dev/null
@@ -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 (file)
index 0000000..aee809b
--- /dev/null
@@ -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"