From 7daaac2bc350e2907554a01eb7eb1286247e5832 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Wed, 9 Oct 2024 21:44:42 +0200 Subject: [PATCH] =?utf8?q?=E2=9C=A8=20Add=20new=20tutorial=20for=20SQL=20d?= =?utf8?q?atabases=20with=20SQLModel=20(#12285)?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- docs/em/docs/advanced/testing-database.md | 101 -- docs/em/docs/how-to/sql-databases-peewee.md | 576 ------------ docs/en/docs/advanced/testing-database.md | 111 --- .../docs/how-to/async-sql-encode-databases.md | 201 ---- .../docs/how-to/nosql-databases-couchbase.md | 178 ---- docs/en/docs/how-to/sql-databases-peewee.md | 594 ------------ docs/en/docs/how-to/testing-database.md | 7 + .../img/tutorial/sql-databases/image01.png | Bin 78949 -> 69755 bytes .../img/tutorial/sql-databases/image02.png | Bin 83482 -> 69197 bytes docs/en/docs/tutorial/sql-databases.md | 888 ++++-------------- docs/en/mkdocs.yml | 9 +- docs/zh/docs/advanced/testing-database.md | 101 -- docs_src/async_sql_databases/tutorial001.py | 65 -- docs_src/nosql_databases/tutorial001.py | 53 -- docs_src/sql_databases/sql_app/__init__.py | 0 docs_src/sql_databases/sql_app/alt_main.py | 62 -- docs_src/sql_databases/sql_app/crud.py | 36 - docs_src/sql_databases/sql_app/database.py | 13 - docs_src/sql_databases/sql_app/main.py | 55 -- docs_src/sql_databases/sql_app/models.py | 26 - docs_src/sql_databases/sql_app/schemas.py | 37 - .../sql_databases/sql_app/tests/__init__.py | 0 .../sql_app/tests/test_sql_app.py | 50 - .../sql_databases/sql_app_py310/__init__.py | 0 .../sql_databases/sql_app_py310/alt_main.py | 60 -- docs_src/sql_databases/sql_app_py310/crud.py | 36 - .../sql_databases/sql_app_py310/database.py | 13 - docs_src/sql_databases/sql_app_py310/main.py | 53 -- .../sql_databases/sql_app_py310/models.py | 26 - .../sql_databases/sql_app_py310/schemas.py | 35 - .../sql_app_py310/tests/__init__.py | 0 .../sql_app_py310/tests/test_sql_app.py | 47 - .../sql_databases/sql_app_py39/__init__.py | 0 .../sql_databases/sql_app_py39/alt_main.py | 60 -- docs_src/sql_databases/sql_app_py39/crud.py | 36 - .../sql_databases/sql_app_py39/database.py | 13 - docs_src/sql_databases/sql_app_py39/main.py | 53 -- docs_src/sql_databases/sql_app_py39/models.py | 26 - .../sql_databases/sql_app_py39/schemas.py | 37 - .../sql_app_py39/tests/__init__.py | 0 .../sql_app_py39/tests/test_sql_app.py | 47 - docs_src/sql_databases/tutorial001.py | 71 ++ docs_src/sql_databases/tutorial001_an.py | 74 ++ .../sql_databases/tutorial001_an_py310.py | 73 ++ docs_src/sql_databases/tutorial001_an_py39.py | 73 ++ docs_src/sql_databases/tutorial001_py310.py | 69 ++ docs_src/sql_databases/tutorial001_py39.py | 71 ++ docs_src/sql_databases/tutorial002.py | 104 ++ docs_src/sql_databases/tutorial002_an.py | 104 ++ .../sql_databases/tutorial002_an_py310.py | 103 ++ docs_src/sql_databases/tutorial002_an_py39.py | 103 ++ docs_src/sql_databases/tutorial002_py310.py | 102 ++ docs_src/sql_databases/tutorial002_py39.py | 104 ++ requirements-docs.txt | 2 +- requirements-tests.txt | 5 +- scripts/playwright/sql_databases/image01.py | 37 + scripts/playwright/sql_databases/image02.py | 37 + .../test_async_sql_databases/__init__.py | 0 .../test_tutorial001.py | 146 --- .../test_sql_databases/test_sql_databases.py | 419 --------- .../test_sql_databases_middleware.py | 421 --------- .../test_sql_databases_middleware_py310.py | 433 --------- .../test_sql_databases_middleware_py39.py | 433 --------- .../test_sql_databases_py310.py | 432 --------- .../test_sql_databases_py39.py | 432 --------- .../test_testing_databases.py | 27 - .../test_testing_databases_py310.py | 28 - .../test_testing_databases_py39.py | 28 - .../test_sql_databases/test_tutorial001.py | 373 ++++++++ .../test_sql_databases/test_tutorial002.py | 481 ++++++++++ 70 files changed, 2154 insertions(+), 6336 deletions(-) delete mode 100644 docs/em/docs/advanced/testing-database.md delete mode 100644 docs/em/docs/how-to/sql-databases-peewee.md delete mode 100644 docs/en/docs/advanced/testing-database.md delete mode 100644 docs/en/docs/how-to/async-sql-encode-databases.md delete mode 100644 docs/en/docs/how-to/nosql-databases-couchbase.md delete mode 100644 docs/en/docs/how-to/sql-databases-peewee.md create mode 100644 docs/en/docs/how-to/testing-database.md delete mode 100644 docs/zh/docs/advanced/testing-database.md delete mode 100644 docs_src/async_sql_databases/tutorial001.py delete mode 100644 docs_src/nosql_databases/tutorial001.py delete mode 100644 docs_src/sql_databases/sql_app/__init__.py delete mode 100644 docs_src/sql_databases/sql_app/alt_main.py delete mode 100644 docs_src/sql_databases/sql_app/crud.py delete mode 100644 docs_src/sql_databases/sql_app/database.py delete mode 100644 docs_src/sql_databases/sql_app/main.py delete mode 100644 docs_src/sql_databases/sql_app/models.py delete mode 100644 docs_src/sql_databases/sql_app/schemas.py delete mode 100644 docs_src/sql_databases/sql_app/tests/__init__.py delete mode 100644 docs_src/sql_databases/sql_app/tests/test_sql_app.py delete mode 100644 docs_src/sql_databases/sql_app_py310/__init__.py delete mode 100644 docs_src/sql_databases/sql_app_py310/alt_main.py delete mode 100644 docs_src/sql_databases/sql_app_py310/crud.py delete mode 100644 docs_src/sql_databases/sql_app_py310/database.py delete mode 100644 docs_src/sql_databases/sql_app_py310/main.py delete mode 100644 docs_src/sql_databases/sql_app_py310/models.py delete mode 100644 docs_src/sql_databases/sql_app_py310/schemas.py delete mode 100644 docs_src/sql_databases/sql_app_py310/tests/__init__.py delete mode 100644 docs_src/sql_databases/sql_app_py310/tests/test_sql_app.py delete mode 100644 docs_src/sql_databases/sql_app_py39/__init__.py delete mode 100644 docs_src/sql_databases/sql_app_py39/alt_main.py delete mode 100644 docs_src/sql_databases/sql_app_py39/crud.py delete mode 100644 docs_src/sql_databases/sql_app_py39/database.py delete mode 100644 docs_src/sql_databases/sql_app_py39/main.py delete mode 100644 docs_src/sql_databases/sql_app_py39/models.py delete mode 100644 docs_src/sql_databases/sql_app_py39/schemas.py delete mode 100644 docs_src/sql_databases/sql_app_py39/tests/__init__.py delete mode 100644 docs_src/sql_databases/sql_app_py39/tests/test_sql_app.py create mode 100644 docs_src/sql_databases/tutorial001.py create mode 100644 docs_src/sql_databases/tutorial001_an.py create mode 100644 docs_src/sql_databases/tutorial001_an_py310.py create mode 100644 docs_src/sql_databases/tutorial001_an_py39.py create mode 100644 docs_src/sql_databases/tutorial001_py310.py create mode 100644 docs_src/sql_databases/tutorial001_py39.py create mode 100644 docs_src/sql_databases/tutorial002.py create mode 100644 docs_src/sql_databases/tutorial002_an.py create mode 100644 docs_src/sql_databases/tutorial002_an_py310.py create mode 100644 docs_src/sql_databases/tutorial002_an_py39.py create mode 100644 docs_src/sql_databases/tutorial002_py310.py create mode 100644 docs_src/sql_databases/tutorial002_py39.py create mode 100644 scripts/playwright/sql_databases/image01.py create mode 100644 scripts/playwright/sql_databases/image02.py delete mode 100644 tests/test_tutorial/test_async_sql_databases/__init__.py delete mode 100644 tests/test_tutorial/test_async_sql_databases/test_tutorial001.py delete mode 100644 tests/test_tutorial/test_sql_databases/test_sql_databases.py delete mode 100644 tests/test_tutorial/test_sql_databases/test_sql_databases_middleware.py delete mode 100644 tests/test_tutorial/test_sql_databases/test_sql_databases_middleware_py310.py delete mode 100644 tests/test_tutorial/test_sql_databases/test_sql_databases_middleware_py39.py delete mode 100644 tests/test_tutorial/test_sql_databases/test_sql_databases_py310.py delete mode 100644 tests/test_tutorial/test_sql_databases/test_sql_databases_py39.py delete mode 100644 tests/test_tutorial/test_sql_databases/test_testing_databases.py delete mode 100644 tests/test_tutorial/test_sql_databases/test_testing_databases_py310.py delete mode 100644 tests/test_tutorial/test_sql_databases/test_testing_databases_py39.py create mode 100644 tests/test_tutorial/test_sql_databases/test_tutorial001.py create mode 100644 tests/test_tutorial/test_sql_databases/test_tutorial002.py diff --git a/docs/em/docs/advanced/testing-database.md b/docs/em/docs/advanced/testing-database.md deleted file mode 100644 index 71b29f9b7c..0000000000 --- a/docs/em/docs/advanced/testing-database.md +++ /dev/null @@ -1,101 +0,0 @@ -# 🔬 💽 - -👆 💪 ⚙️ 🎏 🔗 🔐 ⚪️➡️ [🔬 🔗 ⏮️ 🔐](testing-dependencies.md){.internal-link target=_blank} 📉 💽 🔬. - -👆 💪 💚 ⚒ 🆙 🎏 💽 🔬, 💾 💽 ⏮️ 💯, 🏤-🥧 ⚫️ ⏮️ 🔬 💽, ♒️. - -👑 💭 ⚫️❔ 🎏 👆 👀 👈 ⏮️ 📃. - -## 🚮 💯 🗄 📱 - -➡️ ℹ 🖼 ⚪️➡️ [🗄 (🔗) 💽](../tutorial/sql-databases.md){.internal-link target=_blank} ⚙️ 🔬 💽. - -🌐 📱 📟 🎏, 👆 💪 🚶 🔙 👈 📃 ✅ ❔ ⚫️. - -🕴 🔀 📥 🆕 🔬 📁. - -👆 😐 🔗 `get_db()` 🔜 📨 💽 🎉. - -💯, 👆 💪 ⚙️ 🔗 🔐 📨 👆 *🛃* 💽 🎉 ↩️ 1️⃣ 👈 🔜 ⚙️ 🛎. - -👉 🖼 👥 🔜 ✍ 🍕 💽 🕴 💯. - -## 📁 📊 - -👥 ✍ 🆕 📁 `sql_app/tests/test_sql_app.py`. - -🆕 📁 📊 👀 💖: - -``` hl_lines="9-11" -. -└── sql_app - ├── __init__.py - ├── crud.py - ├── database.py - ├── main.py - ├── models.py - ├── schemas.py - └── tests - ├── __init__.py - └── test_sql_app.py -``` - -## ✍ 🆕 💽 🎉 - -🥇, 👥 ✍ 🆕 💽 🎉 ⏮️ 🆕 💽. - -💯 👥 🔜 ⚙️ 📁 `test.db` ↩️ `sql_app.db`. - -✋️ 🎂 🎉 📟 🌅 ⚖️ 🌘 🎏, 👥 📁 ⚫️. - -```Python hl_lines="8-13" -{!../../docs_src/sql_databases/sql_app/tests/test_sql_app.py!} -``` - -/// tip - -👆 💪 📉 ❎ 👈 📟 🚮 ⚫️ 🔢 & ⚙️ ⚫️ ⚪️➡️ 👯‍♂️ `database.py` & `tests/test_sql_app.py`. - -🦁 & 🎯 🔛 🎯 🔬 📟, 👥 🖨 ⚫️. - -/// - -## ✍ 💽 - -↩️ 🔜 👥 🔜 ⚙️ 🆕 💽 🆕 📁, 👥 💪 ⚒ 💭 👥 ✍ 💽 ⏮️: - -```Python -Base.metadata.create_all(bind=engine) -``` - -👈 🛎 🤙 `main.py`, ✋️ ⏸ `main.py` ⚙️ 💽 📁 `sql_app.db`, & 👥 💪 ⚒ 💭 👥 ✍ `test.db` 💯. - -👥 🚮 👈 ⏸ 📥, ⏮️ 🆕 📁. - -```Python hl_lines="16" -{!../../docs_src/sql_databases/sql_app/tests/test_sql_app.py!} -``` - -## 🔗 🔐 - -🔜 👥 ✍ 🔗 🔐 & 🚮 ⚫️ 🔐 👆 📱. - -```Python hl_lines="19-24 27" -{!../../docs_src/sql_databases/sql_app/tests/test_sql_app.py!} -``` - -/// tip - -📟 `override_get_db()` 🌖 ⚫️❔ 🎏 `get_db()`, ✋️ `override_get_db()` 👥 ⚙️ `TestingSessionLocal` 🔬 💽 ↩️. - -/// - -## 💯 📱 - -⤴️ 👥 💪 💯 📱 🛎. - -```Python hl_lines="32-47" -{!../../docs_src/sql_databases/sql_app/tests/test_sql_app.py!} -``` - -& 🌐 🛠️ 👥 ⚒ 💽 ⏮️ 💯 🔜 `test.db` 💽 ↩️ 👑 `sql_app.db`. diff --git a/docs/em/docs/how-to/sql-databases-peewee.md b/docs/em/docs/how-to/sql-databases-peewee.md deleted file mode 100644 index d25b77894b..0000000000 --- a/docs/em/docs/how-to/sql-databases-peewee.md +++ /dev/null @@ -1,576 +0,0 @@ -# 🗄 (🔗) 💽 ⏮️ 🏒 - -/// warning - -🚥 👆 ▶️, 🔰 [🗄 (🔗) 💽](../tutorial/sql-databases.md){.internal-link target=_blank} 👈 ⚙️ 🇸🇲 🔜 🥃. - -💭 🆓 🚶 👉. - -/// - -🚥 👆 ▶️ 🏗 ⚪️➡️ 🖌, 👆 🎲 👻 📆 ⏮️ 🇸🇲 🐜 ([🗄 (🔗) 💽](../tutorial/sql-databases.md){.internal-link target=_blank}), ⚖️ 🙆 🎏 🔁 🐜. - -🚥 👆 ⏪ ✔️ 📟 🧢 👈 ⚙️ 🏒 🐜, 👆 💪 ✅ 📥 ❔ ⚙️ ⚫️ ⏮️ **FastAPI**. - -/// warning | "🐍 3️⃣.7️⃣ ➕ ✔" - -👆 🔜 💪 🐍 3️⃣.7️⃣ ⚖️ 🔛 🔒 ⚙️ 🏒 ⏮️ FastAPI. - -/// - -## 🏒 🔁 - -🏒 🚫 🔧 🔁 🛠️, ⚖️ ⏮️ 👫 🤯. - -🏒 ✔️ 🏋️ 🔑 🔃 🚮 🔢 & 🔃 ❔ ⚫️ 🔜 ⚙️. - -🚥 👆 🛠️ 🈸 ⏮️ 🗝 🚫-🔁 🛠️, & 💪 👷 ⏮️ 🌐 🚮 🔢, **⚫️ 💪 👑 🧰**. - -✋️ 🚥 👆 💪 🔀 🔢, 🐕‍🦺 🌖 🌘 1️⃣ 🔁 💽, 👷 ⏮️ 🔁 🛠️ (💖 FastAPI), ♒️, 👆 🔜 💪 🚮 🏗 ➕ 📟 🔐 👈 🔢. - -👐, ⚫️ 💪 ⚫️, & 📥 👆 🔜 👀 ⚫️❔ ⚫️❔ 📟 👆 ✔️ 🚮 💪 ⚙️ 🏒 ⏮️ FastAPI. - -/// note | "📡 ℹ" - -👆 💪 ✍ 🌅 🔃 🏒 🧍 🔃 🔁 🐍 🩺, ❔, 🇵🇷. - -/// - -## 🎏 📱 - -👥 🔜 ✍ 🎏 🈸 🇸🇲 🔰 ([🗄 (🔗) 💽](../tutorial/sql-databases.md){.internal-link target=_blank}). - -🌅 📟 🤙 🎏. - -, 👥 🔜 🎯 🕴 🔛 🔺. - -## 📁 📊 - -➡️ 💬 👆 ✔️ 📁 📛 `my_super_project` 👈 🔌 🎧-📁 🤙 `sql_app` ⏮️ 📊 💖 👉: - -``` -. -└── sql_app - ├── __init__.py - ├── crud.py - ├── database.py - ├── main.py - └── schemas.py -``` - -👉 🌖 🎏 📊 👥 ✔️ 🇸🇲 🔰. - -🔜 ➡️ 👀 ⚫️❔ 🔠 📁/🕹 🔨. - -## ✍ 🏒 🍕 - -➡️ 🔗 📁 `sql_app/database.py`. - -### 🐩 🏒 📟 - -➡️ 🥇 ✅ 🌐 😐 🏒 📟, ✍ 🏒 💽: - -```Python hl_lines="3 5 22" -{!../../docs_src/sql_databases_peewee/sql_app/database.py!} -``` - -/// tip - -✔️ 🤯 👈 🚥 👆 💚 ⚙️ 🎏 💽, 💖 ✳, 👆 🚫 🚫 🔀 🎻. 👆 🔜 💪 ⚙️ 🎏 🏒 💽 🎓. - -/// - -#### 🗒 - -❌: - -```Python -check_same_thread=False -``` - -🌓 1️⃣ 🇸🇲 🔰: - -```Python -connect_args={"check_same_thread": False} -``` - -...⚫️ 💪 🕴 `SQLite`. - -/// info | "📡 ℹ" - -⚫️❔ 🎏 📡 ℹ [🗄 (🔗) 💽](../tutorial/sql-databases.md#_7){.internal-link target=_blank} ✔. - -/// - -### ⚒ 🏒 🔁-🔗 `PeeweeConnectionState` - -👑 ❔ ⏮️ 🏒 & FastAPI 👈 🏒 ⚓️ 🙇 🔛 🐍 `threading.local`, & ⚫️ 🚫 ✔️ 🎯 🌌 🔐 ⚫️ ⚖️ ➡️ 👆 🍵 🔗/🎉 🔗 (🔨 🇸🇲 🔰). - -& `threading.local` 🚫 🔗 ⏮️ 🆕 🔁 ⚒ 🏛 🐍. - -/// note | "📡 ℹ" - -`threading.local` ⚙️ ✔️ "🎱" 🔢 👈 ✔️ 🎏 💲 🔠 🧵. - -👉 ⚠ 🗝 🛠️ 🏗 ✔️ 1️⃣ 👁 🧵 📍 📨, 🙅‍♂ 🌖, 🙅‍♂ 🌘. - -⚙️ 👉, 🔠 📨 🔜 ✔️ 🚮 👍 💽 🔗/🎉, ❔ ☑ 🏁 🥅. - -✋️ FastAPI, ⚙️ 🆕 🔁 ⚒, 💪 🍵 🌅 🌘 1️⃣ 📨 🔛 🎏 🧵. & 🎏 🕰, 👁 📨, ⚫️ 💪 🏃 💗 👜 🎏 🧵 (🧵), ⚓️ 🔛 🚥 👆 ⚙️ `async def` ⚖️ 😐 `def`. 👉 ⚫️❔ 🤝 🌐 🎭 📈 FastAPI. - -/// - -✋️ 🐍 3️⃣.7️⃣ & 🔛 🚚 🌖 🏧 🎛 `threading.local`, 👈 💪 ⚙️ 🥉 🌐❔ `threading.local` 🔜 ⚙️, ✋️ 🔗 ⏮️ 🆕 🔁 ⚒. - -👥 🔜 ⚙️ 👈. ⚫️ 🤙 `contextvars`. - -👥 🔜 🔐 🔗 🍕 🏒 👈 ⚙️ `threading.local` & ❎ 👫 ⏮️ `contextvars`, ⏮️ 🔗 ℹ. - -👉 5️⃣📆 😑 🍖 🏗 (& ⚫️ 🤙), 👆 🚫 🤙 💪 🍕 🤔 ❔ ⚫️ 👷 ⚙️ ⚫️. - -👥 🔜 ✍ `PeeweeConnectionState`: - -```Python hl_lines="10-19" -{!../../docs_src/sql_databases_peewee/sql_app/database.py!} -``` - -👉 🎓 😖 ⚪️➡️ 🎁 🔗 🎓 ⚙️ 🏒. - -⚫️ ✔️ 🌐 ⚛ ⚒ 🏒 ⚙️ `contextvars` ↩️ `threading.local`. - -`contextvars` 👷 🍖 🎏 🌘 `threading.local`. ✋️ 🎂 🏒 🔗 📟 🤔 👈 👉 🎓 👷 ⏮️ `threading.local`. - -, 👥 💪 ➕ 🎱 ⚒ ⚫️ 👷 🚥 ⚫️ ⚙️ `threading.local`. `__init__`, `__setattr__`, & `__getattr__` 🛠️ 🌐 ✔ 🎱 👉 ⚙️ 🏒 🍵 🤔 👈 ⚫️ 🔜 🔗 ⏮️ FastAPI. - -/// tip - -👉 🔜 ⚒ 🏒 🎭 ☑ 🕐❔ ⚙️ ⏮️ FastAPI. 🚫 🎲 📂 ⚖️ 📪 🔗 👈 ➖ ⚙️, 🏗 ❌, ♒️. - -✋️ ⚫️ 🚫 🤝 🏒 🔁 💎-🏋️. 👆 🔜 ⚙️ 😐 `def` 🔢 & 🚫 `async def`. - -/// - -### ⚙️ 🛃 `PeeweeConnectionState` 🎓 - -🔜, 📁 `._state` 🔗 🔢 🏒 💽 `db` 🎚 ⚙️ 🆕 `PeeweeConnectionState`: - -```Python hl_lines="24" -{!../../docs_src/sql_databases_peewee/sql_app/database.py!} -``` - -/// tip - -⚒ 💭 👆 📁 `db._state` *⏮️* 🏗 `db`. - -/// - -/// tip - -👆 🔜 🎏 🙆 🎏 🏒 💽, 🔌 `PostgresqlDatabase`, `MySQLDatabase`, ♒️. - -/// - -## ✍ 💽 🏷 - -➡️ 🔜 👀 📁 `sql_app/models.py`. - -### ✍ 🏒 🏷 👆 💽 - -🔜 ✍ 🏒 🏷 (🎓) `User` & `Item`. - -👉 🎏 👆 🔜 🚥 👆 ⏩ 🏒 🔰 & ℹ 🏷 ✔️ 🎏 💽 🇸🇲 🔰. - -/// tip - -🏒 ⚙️ ⚖ "**🏷**" 🔗 👉 🎓 & 👐 👈 🔗 ⏮️ 💽. - -✋️ Pydantic ⚙️ ⚖ "**🏷**" 🔗 🕳 🎏, 💽 🔬, 🛠️, & 🧾 🎓 & 👐. - -/// - -🗄 `db` ⚪️➡️ `database` (📁 `database.py` ⚪️➡️ 🔛) & ⚙️ ⚫️ 📥. - -```Python hl_lines="3 6-12 15-21" -{!../../docs_src/sql_databases_peewee/sql_app/models.py!} -``` - -/// tip - -🏒 ✍ 📚 🎱 🔢. - -⚫️ 🔜 🔁 🚮 `id` 🔢 🔢 👑 🔑. - -⚫️ 🔜 ⚒ 📛 🏓 ⚓️ 🔛 🎓 📛. - - `Item`, ⚫️ 🔜 ✍ 🔢 `owner_id` ⏮️ 🔢 🆔 `User`. ✋️ 👥 🚫 📣 ⚫️ 🙆. - -/// - -## ✍ Pydantic 🏷 - -🔜 ➡️ ✅ 📁 `sql_app/schemas.py`. - -/// tip - -❎ 😨 🖖 🏒 *🏷* & Pydantic *🏷*, 👥 🔜 ✔️ 📁 `models.py` ⏮️ 🏒 🏷, & 📁 `schemas.py` ⏮️ Pydantic 🏷. - -👫 Pydantic 🏷 🔬 🌅 ⚖️ 🌘 "🔗" (☑ 📊 💠). - -👉 🔜 ℹ 👥 ❎ 😨 ⏪ ⚙️ 👯‍♂️. - -/// - -### ✍ Pydantic *🏷* / 🔗 - -✍ 🌐 🎏 Pydantic 🏷 🇸🇲 🔰: - -```Python hl_lines="16-18 21-22 25-30 34-35 38-39 42-48" -{!../../docs_src/sql_databases_peewee/sql_app/schemas.py!} -``` - -/// tip - -📥 👥 🏗 🏷 ⏮️ `id`. - -👥 🚫 🎯 ✔ `id` 🔢 🏒 🏷, ✋️ 🏒 🚮 1️⃣ 🔁. - -👥 ❎ 🎱 `owner_id` 🔢 `Item`. - -/// - -### ✍ `PeeweeGetterDict` Pydantic *🏷* / 🔗 - -🕐❔ 👆 🔐 💛 🏒 🎚, 💖 `some_user.items`, 🏒 🚫 🚚 `list` `Item`. - -⚫️ 🚚 🎁 🛃 🎚 🎓 `ModelSelect`. - -⚫️ 💪 ✍ `list` 🚮 🏬 ⏮️ `list(some_user.items)`. - -✋️ 🎚 ⚫️ 🚫 `list`. & ⚫️ 🚫 ☑ 🐍 🚂. ↩️ 👉, Pydantic 🚫 💭 🔢 ❔ 🗜 ⚫️ `list` Pydantic *🏷* / 🔗. - -✋️ ⏮️ ⏬ Pydantic ✔ 🚚 🛃 🎓 👈 😖 ⚪️➡️ `pydantic.utils.GetterDict`, 🚚 🛠️ ⚙️ 🕐❔ ⚙️ `orm_mode = True` 🗃 💲 🐜 🏷 🔢. - -👥 🔜 ✍ 🛃 `PeeweeGetterDict` 🎓 & ⚙️ ⚫️ 🌐 🎏 Pydantic *🏷* / 🔗 👈 ⚙️ `orm_mode`: - -```Python hl_lines="3 8-13 31 49" -{!../../docs_src/sql_databases_peewee/sql_app/schemas.py!} -``` - -📥 👥 ✅ 🚥 🔢 👈 ➖ 🔐 (✅ `.items` `some_user.items`) 👐 `peewee.ModelSelect`. - -& 🚥 👈 💼, 📨 `list` ⏮️ ⚫️. - -& ⤴️ 👥 ⚙️ ⚫️ Pydantic *🏷* / 🔗 👈 ⚙️ `orm_mode = True`, ⏮️ 📳 🔢 `getter_dict = PeeweeGetterDict`. - -/// tip - -👥 🕴 💪 ✍ 1️⃣ `PeeweeGetterDict` 🎓, & 👥 💪 ⚙️ ⚫️ 🌐 Pydantic *🏷* / 🔗. - -/// - -## 💩 🇨🇻 - -🔜 ➡️ 👀 📁 `sql_app/crud.py`. - -### ✍ 🌐 💩 🇨🇻 - -✍ 🌐 🎏 💩 🇨🇻 🇸🇲 🔰, 🌐 📟 📶 🎏: - -```Python hl_lines="1 4-5 8-9 12-13 16-20 23-24 27-30" -{!../../docs_src/sql_databases_peewee/sql_app/crud.py!} -``` - -📤 🔺 ⏮️ 📟 🇸🇲 🔰. - -👥 🚫 🚶‍♀️ `db` 🔢 🤭. ↩️ 👥 ⚙️ 🏷 🔗. 👉 ↩️ `db` 🎚 🌐 🎚, 👈 🔌 🌐 🔗 ⚛. 👈 ⚫️❔ 👥 ✔️ 🌐 `contextvars` ℹ 🔛. - -🆖, 🕐❔ 🛬 📚 🎚, 💖 `get_users`, 👥 🔗 🤙 `list`, 💖: - -```Python -list(models.User.select()) -``` - -👉 🎏 🤔 👈 👥 ✔️ ✍ 🛃 `PeeweeGetterDict`. ✋️ 🛬 🕳 👈 ⏪ `list` ↩️ `peewee.ModelSelect` `response_model` *➡ 🛠️* ⏮️ `List[models.User]` (👈 👥 🔜 👀 ⏪) 🔜 👷 ☑. - -## 👑 **FastAPI** 📱 - -& 🔜 📁 `sql_app/main.py` ➡️ 🛠️ & ⚙️ 🌐 🎏 🍕 👥 ✍ ⏭. - -### ✍ 💽 🏓 - -📶 🙃 🌌 ✍ 💽 🏓: - -```Python hl_lines="9-11" -{!../../docs_src/sql_databases_peewee/sql_app/main.py!} -``` - -### ✍ 🔗 - -✍ 🔗 👈 🔜 🔗 💽 ▶️️ ▶️ 📨 & 🔌 ⚫️ 🔚: - -```Python hl_lines="23-29" -{!../../docs_src/sql_databases_peewee/sql_app/main.py!} -``` - -📥 👥 ✔️ 🛁 `yield` ↩️ 👥 🤙 🚫 ⚙️ 💽 🎚 🔗. - -⚫️ 🔗 💽 & ♻ 🔗 💽 🔗 🔢 👈 🔬 🔠 📨 (⚙️ `contextvars` 🎱 ⚪️➡️ 🔛). - -↩️ 💽 🔗 ⚠ 👤/🅾 🚧, 👉 🔗 ✍ ⏮️ 😐 `def` 🔢. - -& ⤴️, 🔠 *➡ 🛠️ 🔢* 👈 💪 🔐 💽 👥 🚮 ⚫️ 🔗. - -✋️ 👥 🚫 ⚙️ 💲 👐 👉 🔗 (⚫️ 🤙 🚫 🤝 🙆 💲, ⚫️ ✔️ 🛁 `yield`). , 👥 🚫 🚮 ⚫️ *➡ 🛠️ 🔢* ✋️ *➡ 🛠️ 👨‍🎨* `dependencies` 🔢: - -```Python hl_lines="32 40 47 59 65 72" -{!../../docs_src/sql_databases_peewee/sql_app/main.py!} -``` - -### 🔑 🔢 🎧-🔗 - -🌐 `contextvars` 🍕 👷, 👥 💪 ⚒ 💭 👥 ✔️ 🔬 💲 `ContextVar` 🔠 📨 👈 ⚙️ 💽, & 👈 💲 🔜 ⚙️ 💽 🇵🇸 (🔗, 💵, ♒️) 🎂 📨. - -👈, 👥 💪 ✍ ➕1️⃣ `async` 🔗 `reset_db_state()` 👈 ⚙️ 🎧-🔗 `get_db()`. ⚫️ 🔜 ⚒ 💲 🔑 🔢 (⏮️ 🔢 `dict`) 👈 🔜 ⚙️ 💽 🇵🇸 🎂 📨. & ⤴️ 🔗 `get_db()` 🔜 🏪 ⚫️ 💽 🇵🇸 (🔗, 💵, ♒️). - -```Python hl_lines="18-20" -{!../../docs_src/sql_databases_peewee/sql_app/main.py!} -``` - -**⏭ 📨**, 👥 🔜 ⏲ 👈 🔑 🔢 🔄 `async` 🔗 `reset_db_state()` & ⤴️ ✍ 🆕 🔗 `get_db()` 🔗, 👈 🆕 📨 🔜 ✔️ 🚮 👍 💽 🇵🇸 (🔗, 💵, ♒️). - -/// tip - -FastAPI 🔁 🛠️, 1️⃣ 📨 💪 ▶️ ➖ 🛠️, & ⏭ 🏁, ➕1️⃣ 📨 💪 📨 & ▶️ 🏭 👍, & ⚫️ 🌐 💪 🛠️ 🎏 🧵. - -✋️ 🔑 🔢 🤔 👫 🔁 ⚒,, 🏒 💽 🇵🇸 ⚒ `async` 🔗 `reset_db_state()` 🔜 🚧 🚮 👍 💽 🎂 🎂 📨. - - & 🎏 🕰, 🎏 🛠️ 📨 🔜 ✔️ 🚮 👍 💽 🇵🇸 👈 🔜 🔬 🎂 📨. - -/// - -#### 🏒 🗳 - -🚥 👆 ⚙️ 🏒 🗳, ☑ 💽 `db.obj`. - -, 👆 🔜 ⏲ ⚫️ ⏮️: - -```Python hl_lines="3-4" -async def reset_db_state(): - database.db.obj._state._state.set(db_state_default.copy()) - database.db.obj._state.reset() -``` - -### ✍ 👆 **FastAPI** *➡ 🛠️* - -🔜, 😒, 📥 🐩 **FastAPI** *➡ 🛠️* 📟. - -```Python hl_lines="32-37 40-43 46-53 56-62 65-68 71-79" -{!../../docs_src/sql_databases_peewee/sql_app/main.py!} -``` - -### 🔃 `def` 🆚 `async def` - -🎏 ⏮️ 🇸🇲, 👥 🚫 🔨 🕳 💖: - -```Python -user = await models.User.select().first() -``` - -...✋️ ↩️ 👥 ⚙️: - -```Python -user = models.User.select().first() -``` - -, 🔄, 👥 🔜 📣 *➡ 🛠️ 🔢* & 🔗 🍵 `async def`, ⏮️ 😐 `def`,: - -```Python hl_lines="2" -# Something goes here -def read_users(skip: int = 0, limit: int = 100): - # Something goes here -``` - -## 🔬 🏒 ⏮️ 🔁 - -👉 🖼 🔌 ➕ *➡ 🛠️* 👈 🔬 📏 🏭 📨 ⏮️ `time.sleep(sleep_time)`. - -⚫️ 🔜 ✔️ 💽 🔗 📂 ▶️ & 🔜 ⌛ 🥈 ⏭ 🙇 🔙. & 🔠 🆕 📨 🔜 ⌛ 🕐 🥈 🌘. - -👉 🔜 💪 ➡️ 👆 💯 👈 👆 📱 ⏮️ 🏒 & FastAPI 🎭 ☑ ⏮️ 🌐 💩 🔃 🧵. - -🚥 👆 💚 ✅ ❔ 🏒 🔜 💔 👆 📱 🚥 ⚙️ 🍵 🛠️, 🚶 `sql_app/database.py` 📁 & 🏤 ⏸: - -```Python -# db._state = PeeweeConnectionState() -``` - -& 📁 `sql_app/main.py` 📁, 🏤 💪 `async` 🔗 `reset_db_state()` & ❎ ⚫️ ⏮️ `pass`: - -```Python -async def reset_db_state(): -# database.db._state._state.set(db_state_default.copy()) -# database.db._state.reset() - pass -``` - -⤴️ 🏃 👆 📱 ⏮️ Uvicorn: - -
- -```console -$ uvicorn sql_app.main:app --reload - -INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) -``` - -
- -📂 👆 🖥 http://127.0.0.1:8000/docs & ✍ 👩‍❤‍👨 👩‍💻. - -⤴️ 📂 1️⃣0️⃣ 📑 http://127.0.0.1:8000/docs#/default/read_🐌_👩‍💻_slowusers_ = 🎏 🕰. - -🚶 *➡ 🛠️* "🤚 `/slowusers/`" 🌐 📑. ⚙️ "🔄 ⚫️ 👅" 🔼 & 🛠️ 📨 🔠 📑, 1️⃣ ▶️️ ⏮️ 🎏. - -📑 🔜 ⌛ 🍖 & ⤴️ 👫 🔜 🎦 `Internal Server Error`. - -### ⚫️❔ 🔨 - -🥇 📑 🔜 ⚒ 👆 📱 ✍ 🔗 💽 & ⌛ 🥈 ⏭ 🙇 🔙 & 📪 💽 🔗. - -⤴️, 📨 ⏭ 📑, 👆 📱 🔜 ⌛ 🕐 🥈 🌘, & 🔛. - -👉 ⛓ 👈 ⚫️ 🔜 🔚 🆙 🏁 🏁 📑' 📨 ⏪ 🌘 ⏮️ 🕐. - -⤴️ 1️⃣ 🏁 📨 👈 ⌛ 🌘 🥈 🔜 🔄 📂 💽 🔗, ✋️ 1️⃣ 📚 ⏮️ 📨 🎏 📑 🔜 🎲 🍵 🎏 🧵 🥇 🕐, ⚫️ 🔜 ✔️ 🎏 💽 🔗 👈 ⏪ 📂, & 🏒 🔜 🚮 ❌ & 👆 🔜 👀 ⚫️ 📶, & 📨 🔜 ✔️ `Internal Server Error`. - -👉 🔜 🎲 🔨 🌅 🌘 1️⃣ 📚 📑. - -🚥 👆 ✔️ 💗 👩‍💻 💬 👆 📱 ⚫️❔ 🎏 🕰, 👉 ⚫️❔ 💪 🔨. - -& 👆 📱 ▶️ 🍵 🌅 & 🌖 👩‍💻 🎏 🕰, ⌛ 🕰 👁 📨 💪 📏 & 📏 ⏲ ❌. - -### 🔧 🏒 ⏮️ FastAPI - -🔜 🚶 🔙 📁 `sql_app/database.py`, & ✍ ⏸: - -```Python -db._state = PeeweeConnectionState() -``` - -& 📁 `sql_app/main.py` 📁, ✍ 💪 `async` 🔗 `reset_db_state()`: - -```Python -async def reset_db_state(): - database.db._state._state.set(db_state_default.copy()) - database.db._state.reset() -``` - -❎ 👆 🏃‍♂ 📱 & ▶️ ⚫️ 🔄. - -🔁 🎏 🛠️ ⏮️ 1️⃣0️⃣ 📑. 👉 🕰 🌐 👫 🔜 ⌛ & 👆 🔜 🤚 🌐 🏁 🍵 ❌. - -...👆 🔧 ⚫️ ❗ - -## 📄 🌐 📁 - - 💭 👆 🔜 ✔️ 📁 📛 `my_super_project` (⚖️ 👐 👆 💚) 👈 🔌 🎧-📁 🤙 `sql_app`. - -`sql_app` 🔜 ✔️ 📄 📁: - -* `sql_app/__init__.py`: 🛁 📁. - -* `sql_app/database.py`: - -```Python -{!../../docs_src/sql_databases_peewee/sql_app/database.py!} -``` - -* `sql_app/models.py`: - -```Python -{!../../docs_src/sql_databases_peewee/sql_app/models.py!} -``` - -* `sql_app/schemas.py`: - -```Python -{!../../docs_src/sql_databases_peewee/sql_app/schemas.py!} -``` - -* `sql_app/crud.py`: - -```Python -{!../../docs_src/sql_databases_peewee/sql_app/crud.py!} -``` - -* `sql_app/main.py`: - -```Python -{!../../docs_src/sql_databases_peewee/sql_app/main.py!} -``` - -## 📡 ℹ - -/// warning - -👉 📶 📡 ℹ 👈 👆 🎲 🚫 💪. - -/// - -### ⚠ - -🏒 ⚙️ `threading.local` 🔢 🏪 ⚫️ 💽 "🇵🇸" 💽 (🔗, 💵, ♒️). - -`threading.local` ✍ 💲 🌟 ⏮️ 🧵, ✋️ 🔁 🛠️ 🔜 🏃 🌐 📟 (✅ 🔠 📨) 🎏 🧵, & 🎲 🚫 ✔. - -🔛 🔝 👈, 🔁 🛠️ 💪 🏃 🔁 📟 🧵 (⚙️ `asyncio.run_in_executor`), ✋️ 🔗 🎏 📨. - -👉 ⛓ 👈, ⏮️ 🏒 ⏮️ 🛠️, 💗 📋 💪 ⚙️ 🎏 `threading.local` 🔢 & 🔚 🆙 🤝 🎏 🔗 & 💽 (👈 👫 🚫🔜 🚫), & 🎏 🕰, 🚥 👫 🛠️ 🔁 👤/🅾-🚧 📟 🧵 (⏮️ 😐 `def` 🔢 FastAPI, *➡ 🛠️* & 🔗), 👈 📟 🏆 🚫 ✔️ 🔐 💽 🇵🇸 🔢, ⏪ ⚫️ 🍕 🎏 📨 & ⚫️ 🔜 💪 🤚 🔐 🎏 💽 🇵🇸. - -### 🔑 🔢 - -🐍 3️⃣.7️⃣ ✔️ `contextvars` 👈 💪 ✍ 🇧🇿 🔢 📶 🎏 `threading.local`, ✋️ 🔗 👫 🔁 ⚒. - -📤 📚 👜 ✔️ 🤯. - -`ContextVar` ✔️ ✍ 🔝 🕹, 💖: - -```Python -some_var = ContextVar("some_var", default="default value") -``` - -⚒ 💲 ⚙️ ⏮️ "🔑" (✅ ⏮️ 📨) ⚙️: - -```Python -some_var.set("new value") -``` - -🤚 💲 🙆 🔘 🔑 (✅ 🙆 🍕 🚚 ⏮️ 📨) ⚙️: - -```Python -some_var.get() -``` - -### ⚒ 🔑 🔢 `async` 🔗 `reset_db_state()` - -🚥 🍕 🔁 📟 ⚒ 💲 ⏮️ `some_var.set("updated in function")` (✅ 💖 `async` 🔗), 🎂 📟 ⚫️ & 📟 👈 🚶 ⏮️ (✅ 📟 🔘 `async` 🔢 🤙 ⏮️ `await`) 🔜 👀 👈 🆕 💲. - -, 👆 💼, 🚥 👥 ⚒ 🏒 🇵🇸 🔢 (⏮️ 🔢 `dict`) `async` 🔗, 🌐 🎂 🔗 📟 👆 📱 🔜 👀 👉 💲 & 🔜 💪 ♻ ⚫️ 🎂 📨. - -& 🔑 🔢 🔜 ⚒ 🔄 ⏭ 📨, 🚥 👫 🛠️. - -### ⚒ 💽 🇵🇸 🔗 `get_db()` - -`get_db()` 😐 `def` 🔢, **FastAPI** 🔜 ⚒ ⚫️ 🏃 🧵, ⏮️ *📁* "🔑", 🧑‍🤝‍🧑 🎏 💲 🔑 🔢 ( `dict` ⏮️ ⏲ 💽 🇵🇸). ⤴️ ⚫️ 💪 🚮 💽 🇵🇸 👈 `dict`, 💖 🔗, ♒️. - -✋️ 🚥 💲 🔑 🔢 (🔢 `dict`) ⚒ 👈 😐 `def` 🔢, ⚫️ 🔜 ✍ 🆕 💲 👈 🔜 🚧 🕴 👈 🧵 🧵, & 🎂 📟 (💖 *➡ 🛠️ 🔢*) 🚫🔜 ✔️ 🔐 ⚫️. `get_db()` 👥 💪 🕴 ⚒ 💲 `dict`, ✋️ 🚫 🎂 `dict` ⚫️. - -, 👥 💪 ✔️ `async` 🔗 `reset_db_state()` ⚒ `dict` 🔑 🔢. 👈 🌌, 🌐 📟 ✔️ 🔐 🎏 `dict` 💽 🇵🇸 👁 📨. - -### 🔗 & 🔌 🔗 `get_db()` - -⤴️ ⏭ ❔ 🔜, ⚫️❔ 🚫 🔗 & 🔌 💽 `async` 🔗 ⚫️, ↩️ `get_db()`❓ - -`async` 🔗 ✔️ `async` 🔑 🔢 🛡 🎂 📨, ✋️ 🏗 & 📪 💽 🔗 ⚠ 🚧, ⚫️ 💪 📉 🎭 🚥 ⚫️ 📤. - -👥 💪 😐 `def` 🔗 `get_db()`. diff --git a/docs/en/docs/advanced/testing-database.md b/docs/en/docs/advanced/testing-database.md deleted file mode 100644 index 2b704f2299..0000000000 --- a/docs/en/docs/advanced/testing-database.md +++ /dev/null @@ -1,111 +0,0 @@ -# Testing a Database - -/// info - -These docs are about to be updated. 🎉 - -The current version assumes Pydantic v1, and SQLAlchemy versions less than 2.0. - -The new docs will include Pydantic v2 and will use SQLModel (which is also based on SQLAlchemy) once it is updated to use Pydantic v2 as well. - -/// - -You can use the same dependency overrides from [Testing Dependencies with Overrides](testing-dependencies.md){.internal-link target=_blank} to alter a database for testing. - -You could want to set up a different database for testing, rollback the data after the tests, pre-fill it with some testing data, etc. - -The main idea is exactly the same you saw in that previous chapter. - -## Add tests for the SQL app - -Let's update the example from [SQL (Relational) Databases](../tutorial/sql-databases.md){.internal-link target=_blank} to use a testing database. - -All the app code is the same, you can go back to that chapter check how it was. - -The only changes here are in the new testing file. - -Your normal dependency `get_db()` would return a database session. - -In the test, you could use a dependency override to return your *custom* database session instead of the one that would be used normally. - -In this example we'll create a temporary database only for the tests. - -## File structure - -We create a new file at `sql_app/tests/test_sql_app.py`. - -So the new file structure looks like: - -``` hl_lines="9-11" -. -└── sql_app - ├── __init__.py - ├── crud.py - ├── database.py - ├── main.py - ├── models.py - ├── schemas.py - └── tests - ├── __init__.py - └── test_sql_app.py -``` - -## Create the new database session - -First, we create a new database session with the new database. - -We'll use an in-memory database that persists during the tests instead of the local file `sql_app.db`. - -But the rest of the session code is more or less the same, we just copy it. - -```Python hl_lines="8-13" -{!../../docs_src/sql_databases/sql_app/tests/test_sql_app.py!} -``` - -/// tip - -You could reduce duplication in that code by putting it in a function and using it from both `database.py` and `tests/test_sql_app.py`. - -For simplicity and to focus on the specific testing code, we are just copying it. - -/// - -## Create the database - -Because now we are going to use a new database in a new file, we need to make sure we create the database with: - -```Python -Base.metadata.create_all(bind=engine) -``` - -That is normally called in `main.py`, but the line in `main.py` uses the database file `sql_app.db`, and we need to make sure we create `test.db` for the tests. - -So we add that line here, with the new file. - -```Python hl_lines="16" -{!../../docs_src/sql_databases/sql_app/tests/test_sql_app.py!} -``` - -## Dependency override - -Now we create the dependency override and add it to the overrides for our app. - -```Python hl_lines="19-24 27" -{!../../docs_src/sql_databases/sql_app/tests/test_sql_app.py!} -``` - -/// tip - -The code for `override_get_db()` is almost exactly the same as for `get_db()`, but in `override_get_db()` we use the `TestingSessionLocal` for the testing database instead. - -/// - -## Test the app - -Then we can just test the app as normally. - -```Python hl_lines="32-47" -{!../../docs_src/sql_databases/sql_app/tests/test_sql_app.py!} -``` - -And all the modifications we made in the database during the tests will be in the `test.db` database instead of the main `sql_app.db`. diff --git a/docs/en/docs/how-to/async-sql-encode-databases.md b/docs/en/docs/how-to/async-sql-encode-databases.md deleted file mode 100644 index a72316c4dc..0000000000 --- a/docs/en/docs/how-to/async-sql-encode-databases.md +++ /dev/null @@ -1,201 +0,0 @@ -# ~~Async SQL (Relational) Databases with Encode/Databases~~ (deprecated) - -/// info - -These docs are about to be updated. 🎉 - -The current version assumes Pydantic v1. - -The new docs will include Pydantic v2 and will use SQLModel once it is updated to use Pydantic v2 as well. - -/// - -/// warning | "Deprecated" - -This tutorial is deprecated and will be removed in a future version. - -/// - -You can also use `encode/databases` with **FastAPI** to connect to databases using `async` and `await`. - -It is compatible with: - -* PostgreSQL -* MySQL -* SQLite - -In this example, we'll use **SQLite**, because it uses a single file and Python has integrated support. So, you can copy this example and run it as is. - -Later, for your production application, you might want to use a database server like **PostgreSQL**. - -/// tip - -You could adopt ideas from the section about SQLAlchemy ORM ([SQL (Relational) Databases](../tutorial/sql-databases.md){.internal-link target=_blank}), like using utility functions to perform operations in the database, independent of your **FastAPI** code. - -This section doesn't apply those ideas, to be equivalent to the counterpart in Starlette. - -/// - -## Import and set up `SQLAlchemy` - -* Import `SQLAlchemy`. -* Create a `metadata` object. -* Create a table `notes` using the `metadata` object. - -```Python hl_lines="4 14 16-22" -{!../../docs_src/async_sql_databases/tutorial001.py!} -``` - -/// tip - -Notice that all this code is pure SQLAlchemy Core. - -`databases` is not doing anything here yet. - -/// - -## Import and set up `databases` - -* Import `databases`. -* Create a `DATABASE_URL`. -* Create a `database` object. - -```Python hl_lines="3 9 12" -{!../../docs_src/async_sql_databases/tutorial001.py!} -``` - -/// tip - -If you were connecting to a different database (e.g. PostgreSQL), you would need to change the `DATABASE_URL`. - -/// - -## Create the tables - -In this case, we are creating the tables in the same Python file, but in production, you would probably want to create them with Alembic, integrated with migrations, etc. - -Here, this section would run directly, right before starting your **FastAPI** application. - -* Create an `engine`. -* Create all the tables from the `metadata` object. - -```Python hl_lines="25-28" -{!../../docs_src/async_sql_databases/tutorial001.py!} -``` - -## Create models - -Create Pydantic models for: - -* Notes to be created (`NoteIn`). -* Notes to be returned (`Note`). - -```Python hl_lines="31-33 36-39" -{!../../docs_src/async_sql_databases/tutorial001.py!} -``` - -By creating these Pydantic models, the input data will be validated, serialized (converted), and annotated (documented). - -So, you will be able to see it all in the interactive API docs. - -## Connect and disconnect - -* Create your `FastAPI` application. -* Create event handlers to connect and disconnect from the database. - -```Python hl_lines="42 45-47 50-52" -{!../../docs_src/async_sql_databases/tutorial001.py!} -``` - -## Read notes - -Create the *path operation function* to read notes: - -```Python hl_lines="55-58" -{!../../docs_src/async_sql_databases/tutorial001.py!} -``` - -/// note - -Notice that as we communicate with the database using `await`, the *path operation function* is declared with `async`. - -/// - -### Notice the `response_model=List[Note]` - -It uses `typing.List`. - -That documents (and validates, serializes, filters) the output data, as a `list` of `Note`s. - -## Create notes - -Create the *path operation function* to create notes: - -```Python hl_lines="61-65" -{!../../docs_src/async_sql_databases/tutorial001.py!} -``` - -/// info - -In Pydantic v1 the method was called `.dict()`, it was deprecated (but still supported) in Pydantic v2, and renamed to `.model_dump()`. - -The examples here use `.dict()` for compatibility with Pydantic v1, but you should use `.model_dump()` instead if you can use Pydantic v2. - -/// - -/// note - -Notice that as we communicate with the database using `await`, the *path operation function* is declared with `async`. - -/// - -### About `{**note.dict(), "id": last_record_id}` - -`note` is a Pydantic `Note` object. - -`note.dict()` returns a `dict` with its data, something like: - -```Python -{ - "text": "Some note", - "completed": False, -} -``` - -but it doesn't have the `id` field. - -So we create a new `dict`, that contains the key-value pairs from `note.dict()` with: - -```Python -{**note.dict()} -``` - -`**note.dict()` "unpacks" the key value pairs directly, so, `{**note.dict()}` would be, more or less, a copy of `note.dict()`. - -And then, we extend that copy `dict`, adding another key-value pair: `"id": last_record_id`: - -```Python -{**note.dict(), "id": last_record_id} -``` - -So, the final result returned would be something like: - -```Python -{ - "id": 1, - "text": "Some note", - "completed": False, -} -``` - -## Check it - -You can copy this code as is, and see the docs at http://127.0.0.1:8000/docs. - -There you can see all your API documented and interact with it: - - - -## More info - -You can read more about `encode/databases` at its GitHub page. diff --git a/docs/en/docs/how-to/nosql-databases-couchbase.md b/docs/en/docs/how-to/nosql-databases-couchbase.md deleted file mode 100644 index feb4025dd1..0000000000 --- a/docs/en/docs/how-to/nosql-databases-couchbase.md +++ /dev/null @@ -1,178 +0,0 @@ -# ~~NoSQL (Distributed / Big Data) Databases with Couchbase~~ (deprecated) - -/// info - -These docs are about to be updated. 🎉 - -The current version assumes Pydantic v1. - -The new docs will hopefully use Pydantic v2 and will use ODMantic with MongoDB. - -/// - -/// warning | "Deprecated" - -This tutorial is deprecated and will be removed in a future version. - -/// - -**FastAPI** can also be integrated with any NoSQL. - -Here we'll see an example using **Couchbase**, a document based NoSQL database. - -You can adapt it to any other NoSQL database like: - -* **MongoDB** -* **Cassandra** -* **CouchDB** -* **ArangoDB** -* **ElasticSearch**, etc. - -/// tip - -There is an official project generator with **FastAPI** and **Couchbase**, all based on **Docker**, including a frontend and more tools: https://github.com/tiangolo/full-stack-fastapi-couchbase - -/// - -## Import Couchbase components - -For now, don't pay attention to the rest, only the imports: - -```Python hl_lines="3-5" -{!../../docs_src/nosql_databases/tutorial001.py!} -``` - -## Define a constant to use as a "document type" - -We will use it later as a fixed field `type` in our documents. - -This is not required by Couchbase, but is a good practice that will help you afterwards. - -```Python hl_lines="9" -{!../../docs_src/nosql_databases/tutorial001.py!} -``` - -## Add a function to get a `Bucket` - -In **Couchbase**, a bucket is a set of documents, that can be of different types. - -They are generally all related to the same application. - -The analogy in the relational database world would be a "database" (a specific database, not the database server). - -The analogy in **MongoDB** would be a "collection". - -In the code, a `Bucket` represents the main entrypoint of communication with the database. - -This utility function will: - -* Connect to a **Couchbase** cluster (that might be a single machine). - * Set defaults for timeouts. -* Authenticate in the cluster. -* Get a `Bucket` instance. - * Set defaults for timeouts. -* Return it. - -```Python hl_lines="12-21" -{!../../docs_src/nosql_databases/tutorial001.py!} -``` - -## Create Pydantic models - -As **Couchbase** "documents" are actually just "JSON objects", we can model them with Pydantic. - -### `User` model - -First, let's create a `User` model: - -```Python hl_lines="24-28" -{!../../docs_src/nosql_databases/tutorial001.py!} -``` - -We will use this model in our *path operation function*, so, we don't include in it the `hashed_password`. - -### `UserInDB` model - -Now, let's create a `UserInDB` model. - -This will have the data that is actually stored in the database. - -We don't create it as a subclass of Pydantic's `BaseModel` but as a subclass of our own `User`, because it will have all the attributes in `User` plus a couple more: - -```Python hl_lines="31-33" -{!../../docs_src/nosql_databases/tutorial001.py!} -``` - -/// note - -Notice that we have a `hashed_password` and a `type` field that will be stored in the database. - -But it is not part of the general `User` model (the one we will return in the *path operation*). - -/// - -## Get the user - -Now create a function that will: - -* Take a username. -* Generate a document ID from it. -* Get the document with that ID. -* Put the contents of the document in a `UserInDB` model. - -By creating a function that is only dedicated to getting your user from a `username` (or any other parameter) independent of your *path operation function*, you can more easily reuse it in multiple parts and also add unit tests for it: - -```Python hl_lines="36-42" -{!../../docs_src/nosql_databases/tutorial001.py!} -``` - -### f-strings - -If you are not familiar with the `f"userprofile::{username}"`, it is a Python "f-string". - -Any variable that is put inside of `{}` in an f-string will be expanded / injected in the string. - -### `dict` unpacking - -If you are not familiar with the `UserInDB(**result.value)`, it is using `dict` "unpacking". - -It will take the `dict` at `result.value`, and take each of its keys and values and pass them as key-values to `UserInDB` as keyword arguments. - -So, if the `dict` contains: - -```Python -{ - "username": "johndoe", - "hashed_password": "some_hash", -} -``` - -It will be passed to `UserInDB` as: - -```Python -UserInDB(username="johndoe", hashed_password="some_hash") -``` - -## Create your **FastAPI** code - -### Create the `FastAPI` app - -```Python hl_lines="46" -{!../../docs_src/nosql_databases/tutorial001.py!} -``` - -### Create the *path operation function* - -As our code is calling Couchbase and we are not using the experimental Python await support, we should declare our function with normal `def` instead of `async def`. - -Also, Couchbase recommends not using a single `Bucket` object in multiple "threads", so, we can just get the bucket directly and pass it to our utility functions: - -```Python hl_lines="49-53" -{!../../docs_src/nosql_databases/tutorial001.py!} -``` - -## Recap - -You can integrate any third party NoSQL database, just using their standard packages. - -The same applies to any other external tool, system or API. diff --git a/docs/en/docs/how-to/sql-databases-peewee.md b/docs/en/docs/how-to/sql-databases-peewee.md deleted file mode 100644 index e73ca369b1..0000000000 --- a/docs/en/docs/how-to/sql-databases-peewee.md +++ /dev/null @@ -1,594 +0,0 @@ -# ~~SQL (Relational) Databases with Peewee~~ (deprecated) - -/// warning | "Deprecated" - -This tutorial is deprecated and will be removed in a future version. - -/// - -/// warning - -If you are just starting, the tutorial [SQL (Relational) Databases](../tutorial/sql-databases.md){.internal-link target=_blank} that uses SQLAlchemy should be enough. - -Feel free to skip this. - -Peewee is not recommended with FastAPI as it doesn't play well with anything async Python. There are several better alternatives. - -/// - -/// info - -These docs assume Pydantic v1. - -Because Pewee doesn't play well with anything async and there are better alternatives, I won't update these docs for Pydantic v2, they are kept for now only for historical purposes. - -The examples here are no longer tested in CI (as they were before). - -/// - -If you are starting a project from scratch, you are probably better off with SQLAlchemy ORM ([SQL (Relational) Databases](../tutorial/sql-databases.md){.internal-link target=_blank}), or any other async ORM. - -If you already have a code base that uses Peewee ORM, you can check here how to use it with **FastAPI**. - -/// warning | "Python 3.7+ required" - -You will need Python 3.7 or above to safely use Peewee with FastAPI. - -/// - -## Peewee for async - -Peewee was not designed for async frameworks, or with them in mind. - -Peewee has some heavy assumptions about its defaults and about how it should be used. - -If you are developing an application with an older non-async framework, and can work with all its defaults, **it can be a great tool**. - -But if you need to change some of the defaults, support more than one predefined database, work with an async framework (like FastAPI), etc, you will need to add quite some complex extra code to override those defaults. - -Nevertheless, it's possible to do it, and here you'll see exactly what code you have to add to be able to use Peewee with FastAPI. - -/// note | "Technical Details" - -You can read more about Peewee's stand about async in Python in the docs, an issue, a PR. - -/// - -## The same app - -We are going to create the same application as in the SQLAlchemy tutorial ([SQL (Relational) Databases](../tutorial/sql-databases.md){.internal-link target=_blank}). - -Most of the code is actually the same. - -So, we are going to focus only on the differences. - -## File structure - -Let's say you have a directory named `my_super_project` that contains a sub-directory called `sql_app` with a structure like this: - -``` -. -└── sql_app - ├── __init__.py - ├── crud.py - ├── database.py - ├── main.py - └── schemas.py -``` - -This is almost the same structure as we had for the SQLAlchemy tutorial. - -Now let's see what each file/module does. - -## Create the Peewee parts - -Let's refer to the file `sql_app/database.py`. - -### The standard Peewee code - -Let's first check all the normal Peewee code, create a Peewee database: - -```Python hl_lines="3 5 22" -{!../../docs_src/sql_databases_peewee/sql_app/database.py!} -``` - -/// tip - -Keep in mind that if you wanted to use a different database, like PostgreSQL, you couldn't just change the string. You would need to use a different Peewee database class. - -/// - -#### Note - -The argument: - -```Python -check_same_thread=False -``` - -is equivalent to the one in the SQLAlchemy tutorial: - -```Python -connect_args={"check_same_thread": False} -``` - -...it is needed only for `SQLite`. - -/// info | "Technical Details" - -Exactly the same technical details as in [SQL (Relational) Databases](../tutorial/sql-databases.md#note){.internal-link target=_blank} apply. - -/// - -### Make Peewee async-compatible `PeeweeConnectionState` - -The main issue with Peewee and FastAPI is that Peewee relies heavily on Python's `threading.local`, and it doesn't have a direct way to override it or let you handle connections/sessions directly (as is done in the SQLAlchemy tutorial). - -And `threading.local` is not compatible with the new async features of modern Python. - -/// note | "Technical Details" - -`threading.local` is used to have a "magic" variable that has a different value for each thread. - -This was useful in older frameworks designed to have one single thread per request, no more, no less. - -Using this, each request would have its own database connection/session, which is the actual final goal. - -But FastAPI, using the new async features, could handle more than one request on the same thread. And at the same time, for a single request, it could run multiple things in different threads (in a threadpool), depending on if you use `async def` or normal `def`. This is what gives all the performance improvements to FastAPI. - -/// - -But Python 3.7 and above provide a more advanced alternative to `threading.local`, that can also be used in the places where `threading.local` would be used, but is compatible with the new async features. - -We are going to use that. It's called `contextvars`. - -We are going to override the internal parts of Peewee that use `threading.local` and replace them with `contextvars`, with the corresponding updates. - -This might seem a bit complex (and it actually is), you don't really need to completely understand how it works to use it. - -We will create a `PeeweeConnectionState`: - -```Python hl_lines="10-19" -{!../../docs_src/sql_databases_peewee/sql_app/database.py!} -``` - -This class inherits from a special internal class used by Peewee. - -It has all the logic to make Peewee use `contextvars` instead of `threading.local`. - -`contextvars` works a bit differently than `threading.local`. But the rest of Peewee's internal code assumes that this class works with `threading.local`. - -So, we need to do some extra tricks to make it work as if it was just using `threading.local`. The `__init__`, `__setattr__`, and `__getattr__` implement all the required tricks for this to be used by Peewee without knowing that it is now compatible with FastAPI. - -/// tip - -This will just make Peewee behave correctly when used with FastAPI. Not randomly opening or closing connections that are being used, creating errors, etc. - -But it doesn't give Peewee async super-powers. You should still use normal `def` functions and not `async def`. - -/// - -### Use the custom `PeeweeConnectionState` class - -Now, overwrite the `._state` internal attribute in the Peewee database `db` object using the new `PeeweeConnectionState`: - -```Python hl_lines="24" -{!../../docs_src/sql_databases_peewee/sql_app/database.py!} -``` - -/// tip - -Make sure you overwrite `db._state` *after* creating `db`. - -/// - -/// tip - -You would do the same for any other Peewee database, including `PostgresqlDatabase`, `MySQLDatabase`, etc. - -/// - -## Create the database models - -Let's now see the file `sql_app/models.py`. - -### Create Peewee models for our data - -Now create the Peewee models (classes) for `User` and `Item`. - -This is the same you would do if you followed the Peewee tutorial and updated the models to have the same data as in the SQLAlchemy tutorial. - -/// tip - -Peewee also uses the term "**model**" to refer to these classes and instances that interact with the database. - -But Pydantic also uses the term "**model**" to refer to something different, the data validation, conversion, and documentation classes and instances. - -/// - -Import `db` from `database` (the file `database.py` from above) and use it here. - -```Python hl_lines="3 6-12 15-21" -{!../../docs_src/sql_databases_peewee/sql_app/models.py!} -``` - -/// tip - -Peewee creates several magic attributes. - -It will automatically add an `id` attribute as an integer to be the primary key. - -It will chose the name of the tables based on the class names. - -For the `Item`, it will create an attribute `owner_id` with the integer ID of the `User`. But we don't declare it anywhere. - -/// - -## Create the Pydantic models - -Now let's check the file `sql_app/schemas.py`. - -/// tip - -To avoid confusion between the Peewee *models* and the Pydantic *models*, we will have the file `models.py` with the Peewee models, and the file `schemas.py` with the Pydantic models. - -These Pydantic models define more or less a "schema" (a valid data shape). - -So this will help us avoiding confusion while using both. - -/// - -### Create the Pydantic *models* / schemas - -Create all the same Pydantic models as in the SQLAlchemy tutorial: - -```Python hl_lines="16-18 21-22 25-30 34-35 38-39 42-48" -{!../../docs_src/sql_databases_peewee/sql_app/schemas.py!} -``` - -/// tip - -Here we are creating the models with an `id`. - -We didn't explicitly specify an `id` attribute in the Peewee models, but Peewee adds one automatically. - -We are also adding the magic `owner_id` attribute to `Item`. - -/// - -### Create a `PeeweeGetterDict` for the Pydantic *models* / schemas - -When you access a relationship in a Peewee object, like in `some_user.items`, Peewee doesn't provide a `list` of `Item`. - -It provides a special custom object of class `ModelSelect`. - -It's possible to create a `list` of its items with `list(some_user.items)`. - -But the object itself is not a `list`. And it's also not an actual Python generator. Because of this, Pydantic doesn't know by default how to convert it to a `list` of Pydantic *models* / schemas. - -But recent versions of Pydantic allow providing a custom class that inherits from `pydantic.utils.GetterDict`, to provide the functionality used when using the `orm_mode = True` to retrieve the values for ORM model attributes. - -We are going to create a custom `PeeweeGetterDict` class and use it in all the same Pydantic *models* / schemas that use `orm_mode`: - -```Python hl_lines="3 8-13 31 49" -{!../../docs_src/sql_databases_peewee/sql_app/schemas.py!} -``` - -Here we are checking if the attribute that is being accessed (e.g. `.items` in `some_user.items`) is an instance of `peewee.ModelSelect`. - -And if that's the case, just return a `list` with it. - -And then we use it in the Pydantic *models* / schemas that use `orm_mode = True`, with the configuration variable `getter_dict = PeeweeGetterDict`. - -/// tip - -We only need to create one `PeeweeGetterDict` class, and we can use it in all the Pydantic *models* / schemas. - -/// - -## CRUD utils - -Now let's see the file `sql_app/crud.py`. - -### Create all the CRUD utils - -Create all the same CRUD utils as in the SQLAlchemy tutorial, all the code is very similar: - -```Python hl_lines="1 4-5 8-9 12-13 16-20 23-24 27-30" -{!../../docs_src/sql_databases_peewee/sql_app/crud.py!} -``` - -There are some differences with the code for the SQLAlchemy tutorial. - -We don't pass a `db` attribute around. Instead we use the models directly. This is because the `db` object is a global object, that includes all the connection logic. That's why we had to do all the `contextvars` updates above. - -Aso, when returning several objects, like in `get_users`, we directly call `list`, like in: - -```Python -list(models.User.select()) -``` - -This is for the same reason that we had to create a custom `PeeweeGetterDict`. But by returning something that is already a `list` instead of the `peewee.ModelSelect` the `response_model` in the *path operation* with `List[models.User]` (that we'll see later) will work correctly. - -## Main **FastAPI** app - -And now in the file `sql_app/main.py` let's integrate and use all the other parts we created before. - -### Create the database tables - -In a very simplistic way create the database tables: - -```Python hl_lines="9-11" -{!../../docs_src/sql_databases_peewee/sql_app/main.py!} -``` - -### Create a dependency - -Create a dependency that will connect the database right at the beginning of a request and disconnect it at the end: - -```Python hl_lines="23-29" -{!../../docs_src/sql_databases_peewee/sql_app/main.py!} -``` - -Here we have an empty `yield` because we are actually not using the database object directly. - -It is connecting to the database and storing the connection data in an internal variable that is independent for each request (using the `contextvars` tricks from above). - -Because the database connection is potentially I/O blocking, this dependency is created with a normal `def` function. - -And then, in each *path operation function* that needs to access the database we add it as a dependency. - -But we are not using the value given by this dependency (it actually doesn't give any value, as it has an empty `yield`). So, we don't add it to the *path operation function* but to the *path operation decorator* in the `dependencies` parameter: - -```Python hl_lines="32 40 47 59 65 72" -{!../../docs_src/sql_databases_peewee/sql_app/main.py!} -``` - -### Context variable sub-dependency - -For all the `contextvars` parts to work, we need to make sure we have an independent value in the `ContextVar` for each request that uses the database, and that value will be used as the database state (connection, transactions, etc) for the whole request. - -For that, we need to create another `async` dependency `reset_db_state()` that is used as a sub-dependency in `get_db()`. It will set the value for the context variable (with just a default `dict`) that will be used as the database state for the whole request. And then the dependency `get_db()` will store in it the database state (connection, transactions, etc). - -```Python hl_lines="18-20" -{!../../docs_src/sql_databases_peewee/sql_app/main.py!} -``` - -For the **next request**, as we will reset that context variable again in the `async` dependency `reset_db_state()` and then create a new connection in the `get_db()` dependency, that new request will have its own database state (connection, transactions, etc). - -/// tip - -As FastAPI is an async framework, one request could start being processed, and before finishing, another request could be received and start processing as well, and it all could be processed in the same thread. - -But context variables are aware of these async features, so, a Peewee database state set in the `async` dependency `reset_db_state()` will keep its own data throughout the entire request. - -And at the same time, the other concurrent request will have its own database state that will be independent for the whole request. - -/// - -#### Peewee Proxy - -If you are using a Peewee Proxy, the actual database is at `db.obj`. - -So, you would reset it with: - -```Python hl_lines="3-4" -async def reset_db_state(): - database.db.obj._state._state.set(db_state_default.copy()) - database.db.obj._state.reset() -``` - -### Create your **FastAPI** *path operations* - -Now, finally, here's the standard **FastAPI** *path operations* code. - -```Python hl_lines="32-37 40-43 46-53 56-62 65-68 71-79" -{!../../docs_src/sql_databases_peewee/sql_app/main.py!} -``` - -### About `def` vs `async def` - -The same as with SQLAlchemy, we are not doing something like: - -```Python -user = await models.User.select().first() -``` - -...but instead we are using: - -```Python -user = models.User.select().first() -``` - -So, again, we should declare the *path operation functions* and the dependency without `async def`, just with a normal `def`, as: - -```Python hl_lines="2" -# Something goes here -def read_users(skip: int = 0, limit: int = 100): - # Something goes here -``` - -## Testing Peewee with async - -This example includes an extra *path operation* that simulates a long processing request with `time.sleep(sleep_time)`. - -It will have the database connection open at the beginning and will just wait some seconds before replying back. And each new request will wait one second less. - -This will easily let you test that your app with Peewee and FastAPI is behaving correctly with all the stuff about threads. - -If you want to check how Peewee would break your app if used without modification, go the `sql_app/database.py` file and comment the line: - -```Python -# db._state = PeeweeConnectionState() -``` - -And in the file `sql_app/main.py` file, comment the body of the `async` dependency `reset_db_state()` and replace it with a `pass`: - -```Python -async def reset_db_state(): -# database.db._state._state.set(db_state_default.copy()) -# database.db._state.reset() - pass -``` - -Then run your app with Uvicorn: - -
- -```console -$ uvicorn sql_app.main:app --reload - -INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) -``` - -
- -Open your browser at http://127.0.0.1:8000/docs and create a couple of users. - -Then open 10 tabs at http://127.0.0.1:8000/docs#/default/read_slow_users_slowusers__get at the same time. - -Go to the *path operation* "Get `/slowusers/`" in all of the tabs. Use the "Try it out" button and execute the request in each tab, one right after the other. - -The tabs will wait for a bit and then some of them will show `Internal Server Error`. - -### What happens - -The first tab will make your app create a connection to the database and wait for some seconds before replying back and closing the database connection. - -Then, for the request in the next tab, your app will wait for one second less, and so on. - -This means that it will end up finishing some of the last tabs' requests earlier than some of the previous ones. - -Then one the last requests that wait less seconds will try to open a database connection, but as one of those previous requests for the other tabs will probably be handled in the same thread as the first one, it will have the same database connection that is already open, and Peewee will throw an error and you will see it in the terminal, and the response will have an `Internal Server Error`. - -This will probably happen for more than one of those tabs. - -If you had multiple clients talking to your app exactly at the same time, this is what could happen. - -And as your app starts to handle more and more clients at the same time, the waiting time in a single request needs to be shorter and shorter to trigger the error. - -### Fix Peewee with FastAPI - -Now go back to the file `sql_app/database.py`, and uncomment the line: - -```Python -db._state = PeeweeConnectionState() -``` - -And in the file `sql_app/main.py` file, uncomment the body of the `async` dependency `reset_db_state()`: - -```Python -async def reset_db_state(): - database.db._state._state.set(db_state_default.copy()) - database.db._state.reset() -``` - -Terminate your running app and start it again. - -Repeat the same process with the 10 tabs. This time all of them will wait and you will get all the results without errors. - -...You fixed it! - -## Review all the files - - Remember you should have a directory named `my_super_project` (or however you want) that contains a sub-directory called `sql_app`. - -`sql_app` should have the following files: - -* `sql_app/__init__.py`: is an empty file. - -* `sql_app/database.py`: - -```Python -{!../../docs_src/sql_databases_peewee/sql_app/database.py!} -``` - -* `sql_app/models.py`: - -```Python -{!../../docs_src/sql_databases_peewee/sql_app/models.py!} -``` - -* `sql_app/schemas.py`: - -```Python -{!../../docs_src/sql_databases_peewee/sql_app/schemas.py!} -``` - -* `sql_app/crud.py`: - -```Python -{!../../docs_src/sql_databases_peewee/sql_app/crud.py!} -``` - -* `sql_app/main.py`: - -```Python -{!../../docs_src/sql_databases_peewee/sql_app/main.py!} -``` - -## Technical Details - -/// warning - -These are very technical details that you probably don't need. - -/// - -### The problem - -Peewee uses `threading.local` by default to store it's database "state" data (connection, transactions, etc). - -`threading.local` creates a value exclusive to the current thread, but an async framework would run all the code (e.g. for each request) in the same thread, and possibly not in order. - -On top of that, an async framework could run some sync code in a threadpool (using `asyncio.run_in_executor`), but belonging to the same request. - -This means that, with Peewee's current implementation, multiple tasks could be using the same `threading.local` variable and end up sharing the same connection and data (that they shouldn't), and at the same time, if they execute sync I/O-blocking code in a threadpool (as with normal `def` functions in FastAPI, in *path operations* and dependencies), that code won't have access to the database state variables, even while it's part of the same request and it should be able to get access to the same database state. - -### Context variables - -Python 3.7 has `contextvars` that can create a local variable very similar to `threading.local`, but also supporting these async features. - -There are several things to keep in mind. - -The `ContextVar` has to be created at the top of the module, like: - -```Python -some_var = ContextVar("some_var", default="default value") -``` - -To set a value used in the current "context" (e.g. for the current request) use: - -```Python -some_var.set("new value") -``` - -To get a value anywhere inside of the context (e.g. in any part handling the current request) use: - -```Python -some_var.get() -``` - -### Set context variables in the `async` dependency `reset_db_state()` - -If some part of the async code sets the value with `some_var.set("updated in function")` (e.g. like the `async` dependency), the rest of the code in it and the code that goes after (including code inside of `async` functions called with `await`) will see that new value. - -So, in our case, if we set the Peewee state variable (with a default `dict`) in the `async` dependency, all the rest of the internal code in our app will see this value and will be able to reuse it for the whole request. - -And the context variable would be set again for the next request, even if they are concurrent. - -### Set database state in the dependency `get_db()` - -As `get_db()` is a normal `def` function, **FastAPI** will make it run in a threadpool, with a *copy* of the "context", holding the same value for the context variable (the `dict` with the reset database state). Then it can add database state to that `dict`, like the connection, etc. - -But if the value of the context variable (the default `dict`) was set in that normal `def` function, it would create a new value that would stay only in that thread of the threadpool, and the rest of the code (like the *path operation functions*) wouldn't have access to it. In `get_db()` we can only set values in the `dict`, but not the entire `dict` itself. - -So, we need to have the `async` dependency `reset_db_state()` to set the `dict` in the context variable. That way, all the code has access to the same `dict` for the database state for a single request. - -### Connect and disconnect in the dependency `get_db()` - -Then the next question would be, why not just connect and disconnect the database in the `async` dependency itself, instead of in `get_db()`? - -The `async` dependency has to be `async` for the context variable to be preserved for the rest of the request, but creating and closing the database connection is potentially blocking, so it could degrade performance if it was there. - -So we also need the normal `def` dependency `get_db()`. diff --git a/docs/en/docs/how-to/testing-database.md b/docs/en/docs/how-to/testing-database.md new file mode 100644 index 0000000000..d0ed15bca7 --- /dev/null +++ b/docs/en/docs/how-to/testing-database.md @@ -0,0 +1,7 @@ +# Testing a Database + +You can study about databases, SQL, and SQLModel in the SQLModel docs. 🤓 + +There's a mini tutorial on using SQLModel with FastAPI. ✨ + +That tutorial includes a section about testing SQL databases. 😎 diff --git a/docs/en/docs/img/tutorial/sql-databases/image01.png b/docs/en/docs/img/tutorial/sql-databases/image01.png index 8e575abd65aaf37a33fed5bdae4029955dffb090..bfcdb57a0743c5fdcd453007987a19d878af1f1a 100644 GIT binary patch literal 69755 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
(nw+ZL{GE>k$_wcMe#yPA5BV(MlyXQG6i4-d^O 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
  • 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%0DSFAR<_3wl|$!xIt;av}BcG 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 diff --git a/docs/en/docs/tutorial/sql-databases.md b/docs/en/docs/tutorial/sql-databases.md index 7836efae16..972eb93084 100644 --- a/docs/en/docs/tutorial/sql-databases.md +++ b/docs/en/docs/tutorial/sql-databases.md @@ -1,22 +1,18 @@ # SQL (Relational) Databases -/// info +**FastAPI** doesn't require you to use a SQL (relational) database. But you can use **any database** that you want. -These docs are about to be updated. 🎉 +Here we'll see an example using SQLModel. -The current version assumes Pydantic v1, and SQLAlchemy versions less than 2.0. +**SQLModel** is built on top of SQLAlchemy and Pydantic. It was made by the same author of **FastAPI** to be the perfect match for FastAPI applications that need to use **SQL databases**. -The new docs will include Pydantic v2 and will use SQLModel (which is also based on SQLAlchemy) once it is updated to use Pydantic v2 as well. - -/// - -**FastAPI** doesn't require you to use a SQL (relational) database. +/// tip -But you can use any relational database that you want. +You could use any other SQL or NoSQL database library you want (in some cases called "ORMs"), FastAPI doesn't force you to use anything. 😎 -Here we'll see an example using SQLAlchemy. +/// -You can easily adapt it to any database supported by SQLAlchemy, like: +As SQLModel is based on SQLAlchemy, you can easily use **any database supported** by SQLAlchemy (which makes them also supported by SQLModel), like: * PostgreSQL * MySQL @@ -30,891 +26,335 @@ Later, for your production application, you might want to use a database server /// tip -There is an official project generator with **FastAPI** and **PostgreSQL**, all based on **Docker**, including a frontend and more tools: https://github.com/tiangolo/full-stack-fastapi-postgresql - -/// - -/// note - -Notice that most of the code is the standard `SQLAlchemy` code you would use with any framework. - -The **FastAPI** specific code is as small as always. - -/// - -## ORMs - -**FastAPI** works with any database and any style of library to talk to the database. - -A common pattern is to use an "ORM": an "object-relational mapping" library. - -An ORM has tools to convert ("*map*") between *objects* in code and database tables ("*relations*"). - -With an ORM, you normally create a class that represents a table in a SQL database, each attribute of the class represents a column, with a name and a type. - -For example a class `Pet` could represent a SQL table `pets`. - -And each *instance* object of that class represents a row in the database. - -For example an object `orion_cat` (an instance of `Pet`) could have an attribute `orion_cat.type`, for the column `type`. And the value of that attribute could be, e.g. `"cat"`. - -These ORMs also have tools to make the connections or relations between tables or entities. - -This way, you could also have an attribute `orion_cat.owner` and the owner would contain the data for this pet's owner, taken from the table *owners*. - -So, `orion_cat.owner.name` could be the name (from the `name` column in the `owners` table) of this pet's owner. - -It could have a value like `"Arquilian"`. - -And the ORM will do all the work to get the information from the corresponding table *owners* when you try to access it from your pet object. - -Common ORMs are for example: Django-ORM (part of the Django framework), SQLAlchemy ORM (part of SQLAlchemy, independent of framework) and Peewee (independent of framework), among others. - -Here we will see how to work with **SQLAlchemy ORM**. - -In a similar way you could use any other ORM. - -/// tip - -There's an equivalent article using Peewee here in the docs. +There is an official project generator with **FastAPI** and **PostgreSQL** including a frontend and more tools: https://github.com/fastapi/full-stack-fastapi-template /// -## File structure - -For these examples, let's say you have a directory named `my_super_project` that contains a sub-directory called `sql_app` with a structure like this: - -``` -. -└── sql_app - ├── __init__.py - ├── crud.py - ├── database.py - ├── main.py - ├── models.py - └── schemas.py -``` - -The file `__init__.py` is just an empty file, but it tells Python that `sql_app` with all its modules (Python files) is a package. - -Now let's see what each file/module does. - -## Install `SQLAlchemy` +This is a very simple and short tutorial, if you want to learn about databases in general, about SQL, or more advanced features, go to the SQLModel docs. -First you need to install `SQLAlchemy`. +## Install `SQLModel` -Make sure you create a [virtual environment](../virtual-environments.md){.internal-link target=_blank}, activate it, and then install it, for example: +First, make sure you create your [virtual environment](../virtual-environments.md){.internal-link target=_blank}, activate it, and then install `sqlmodel`:
    ```console -$ pip install sqlalchemy - +$ pip install sqlmodel ---> 100% ```
    -## Create the SQLAlchemy parts - -Let's refer to the file `sql_app/database.py`. +## Create the App with a Single Model -### Import the SQLAlchemy parts +We'll create the simplest first version of the app with a single **SQLModel** model first. -```Python hl_lines="1-3" -{!../../docs_src/sql_databases/sql_app/database.py!} -``` - -### Create a database URL for SQLAlchemy - -```Python hl_lines="5-6" -{!../../docs_src/sql_databases/sql_app/database.py!} -``` - -In this example, we are "connecting" to a SQLite database (opening a file with the SQLite database). - -The file will be located at the same directory in the file `sql_app.db`. - -That's why the last part is `./sql_app.db`. - -If you were using a **PostgreSQL** database instead, you would just have to uncomment the line: - -```Python -SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db" -``` +Later we'll improve it increasing security and versatility with **multiple models** below. 🤓 -...and adapt it with your database data and credentials (equivalently for MySQL, MariaDB or any other). +### Create Models -/// tip - -This is the main line that you would have to modify if you wanted to use a different database. - -/// - -### Create the SQLAlchemy `engine` +Import `SQLModel` and create a database model: -The first step is to create a SQLAlchemy "engine". +{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[1:11] hl[7:11] *} -We will later use this `engine` in other places. +The `Hero` class is very similar to a Pydantic model (in fact, underneath, it actually *is a Pydantic model*). -```Python hl_lines="8-10" -{!../../docs_src/sql_databases/sql_app/database.py!} -``` +There are a few differences: -#### Note +* `table=True` tells SQLModel that this is a *table model*, it should represent a **table** in the SQL database, it's not just a *data model* (as would be any other regular Pydantic class). -The argument: +* `Field(primary_key=True)` tells SQLModel that the `id` is the **primary key** in the SQL database (you can learn more about SQL primary keys in the SQLModel docs). -```Python -connect_args={"check_same_thread": False} -``` + By having the type as `int | None`, SQLModel will know that this column should be an `INTEGER` in the SQL database and that it should be `NULLABLE`. -...is needed only for `SQLite`. It's not needed for other databases. +* `Field(index=True)` tells SQLModel that it should create a **SQL index** for this column, that would allow faster lookups in the database when reading data filtered by this column. -/// info | "Technical Details" + SQLModel will know that something declared as `str` will be a SQL column of type `TEXT` (or `VARCHAR`, depending on the database). -By default SQLite will only allow one thread to communicate with it, assuming that each thread would handle an independent request. +### Create an Engine -This is to prevent accidentally sharing the same connection for different things (for different requests). +A SQLModel `engine` (underneath it's actually a SQLAlchemy `engine`) is what **holds the connections** to the database. -But in FastAPI, using normal functions (`def`) more than one thread could interact with the database for the same request, so we need to make SQLite know that it should allow that with `connect_args={"check_same_thread": False}`. +You would have **one single `engine` object** for all your code to connect to the same database. -Also, we will make sure each request gets its own database connection session in a dependency, so there's no need for that default mechanism. +{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[14:18] hl[14:15,17:18] *} -/// +Using `check_same_thread=False` allows FastAPI to use the same SQLite database in different threads. This is necessary as **one single request** could use **more than one thread** (for example in dependencies). -### Create a `SessionLocal` class +Don't worry, with the way the code is structured, we'll make sure we use **a single SQLModel *session* per request** later, this is actually what the `check_same_thread` is trying to achieve. -Each instance of the `SessionLocal` class will be a database session. The class itself is not a database session yet. +### Create the Tables -But once we create an instance of the `SessionLocal` class, this instance will be the actual database session. +We then add a function that uses `SQLModel.metadata.create_all(engine)` to **create the tables** for all the *table models*. -We name it `SessionLocal` to distinguish it from the `Session` we are importing from SQLAlchemy. +{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[21:22] hl[21:22] *} -We will use `Session` (the one imported from SQLAlchemy) later. +### Create a Session Dependency -To create the `SessionLocal` class, use the function `sessionmaker`: - -```Python hl_lines="11" -{!../../docs_src/sql_databases/sql_app/database.py!} -``` +A **`Session`** is what stores the **objects in memory** and keeps track of any changes needed in the data, then it **uses the `engine`** to communicate with the database. -### Create a `Base` class +We will create a FastAPI **dependency** with `yield` that will provide a new `Session` for each request. This is what ensures that we use a single session per request. 🤓 -Now we will use the function `declarative_base()` that returns a class. +Then we create an `Annotated` dependency `SessionDep` to simplify the rest of the code that will use this dependency. -Later we will inherit from this class to create each of the database models or classes (the ORM models): +{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[25:30] hl[25:27,30] *} -```Python hl_lines="13" -{!../../docs_src/sql_databases/sql_app/database.py!} -``` +### Create Database Tables on Startup -## Create the database models +We will create the database tables when the application starts. -Let's now see the file `sql_app/models.py`. +{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[32:37] hl[35:37] *} -### Create SQLAlchemy models from the `Base` class +Here we create the tables on an application startup event. -We will use this `Base` class we created before to create the SQLAlchemy models. +For production you would probably use a migration script that runs before you start your app. 🤓 /// tip -SQLAlchemy uses the term "**model**" to refer to these classes and instances that interact with the database. - -But Pydantic also uses the term "**model**" to refer to something different, the data validation, conversion, and documentation classes and instances. +SQLModel will have migration utilities wrapping Alembic, but for now, you can use Alembic directly. /// -Import `Base` from `database` (the file `database.py` from above). - -Create classes that inherit from it. - -These classes are the SQLAlchemy models. - -```Python hl_lines="4 7-8 18-19" -{!../../docs_src/sql_databases/sql_app/models.py!} -``` - -The `__tablename__` attribute tells SQLAlchemy the name of the table to use in the database for each of these models. - -### Create model attributes/columns - -Now create all the model (class) attributes. - -Each of these attributes represents a column in its corresponding database table. +### Create a Hero -We use `Column` from SQLAlchemy as the default value. +Because each SQLModel model is also a Pydantic model, you can use it in the same **type annotations** that you could use Pydantic models. -And we pass a SQLAlchemy class "type", as `Integer`, `String`, and `Boolean`, that defines the type in the database, as an argument. +For example, if you declare a parameter of type `Hero`, it will be read from the **JSON body**. -```Python hl_lines="1 10-13 21-24" -{!../../docs_src/sql_databases/sql_app/models.py!} -``` - -### Create the relationships - -Now create the relationships. - -For this, we use `relationship` provided by SQLAlchemy ORM. - -This will become, more or less, a "magic" attribute that will contain the values from other tables related to this one. +The same way, you can declare it as the function's **return type**, and then the shape of the data will show up in the automatic API docs UI. -```Python hl_lines="2 15 26" -{!../../docs_src/sql_databases/sql_app/models.py!} -``` - -When accessing the attribute `items` in a `User`, as in `my_user.items`, it will have a list of `Item` SQLAlchemy models (from the `items` table) that have a foreign key pointing to this record in the `users` table. +{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[40:45] hl[40:45] *} -When you access `my_user.items`, SQLAlchemy will actually go and fetch the items from the database in the `items` table and populate them here. + -And when accessing the attribute `owner` in an `Item`, it will contain a `User` SQLAlchemy model from the `users` table. It will use the `owner_id` attribute/column with its foreign key to know which record to get from the `users` table. +Here we use the `SessionDep` dependency (a `Session`) to add the new `Hero` to the `Session` instance, commit the changes to the database, refresh the data in the `hero`, and then return it. -## Create the Pydantic models +### Read Heroes -Now let's check the file `sql_app/schemas.py`. +We can **read** `Hero`s from the database using a `select()`. We can include a `limit` and `offset` to paginate the results. -/// tip +{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[48:55] hl[51:52,54] *} -To avoid confusion between the SQLAlchemy *models* and the Pydantic *models*, we will have the file `models.py` with the SQLAlchemy models, and the file `schemas.py` with the Pydantic models. +### Read One Hero -These Pydantic models define more or less a "schema" (a valid data shape). +We can **read** a single `Hero`. -So this will help us avoiding confusion while using both. - -/// +{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[58:63] hl[60] *} -### Create initial Pydantic *models* / schemas +### Delete a Hero -Create an `ItemBase` and `UserBase` Pydantic *models* (or let's say "schemas") to have common attributes while creating or reading data. +We can also **delete** a `Hero`. -And create an `ItemCreate` and `UserCreate` that inherit from them (so they will have the same attributes), plus any additional data (attributes) needed for creation. +{* ../../docs_src/sql_databases/tutorial001_an_py310.py ln[66:73] hl[71] *} -So, the user will also have a `password` when creating it. +### Run the App -But for security, the `password` won't be in other Pydantic *models*, for example, it won't be sent from the API when reading a user. +You can run the app: -//// tab | Python 3.10+ - -```Python hl_lines="1 4-6 9-10 21-22 25-26" -{!> ../../docs_src/sql_databases/sql_app_py310/schemas.py!} -``` - -//// - -//// tab | Python 3.9+ - -```Python hl_lines="3 6-8 11-12 23-24 27-28" -{!> ../../docs_src/sql_databases/sql_app_py39/schemas.py!} -``` - -//// - -//// tab | Python 3.8+ - -```Python hl_lines="3 6-8 11-12 23-24 27-28" -{!> ../../docs_src/sql_databases/sql_app/schemas.py!} -``` - -//// - -#### SQLAlchemy style and Pydantic style - -Notice that SQLAlchemy *models* define attributes using `=`, and pass the type as a parameter to `Column`, like in: - -```Python -name = Column(String) -``` +
    -while Pydantic *models* declare the types using `:`, the new type annotation syntax/type hints: +```console +$ fastapi dev main.py -```Python -name: str +INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) ``` -Keep these in mind, so you don't get confused when using `=` and `:` with them. - -### Create Pydantic *models* / schemas for reading / returning - -Now create Pydantic *models* (schemas) that will be used when reading data, when returning it from the API. - -For example, before creating an item, we don't know what will be the ID assigned to it, but when reading it (when returning it from the API) we will already know its ID. +
    -The same way, when reading a user, we can now declare that `items` will contain the items that belong to this user. +Then go to the `/docs` UI, you will see that **FastAPI** is using these **models** to **document** the API, and it will use them to **serialize** and **validate** the data too. -Not only the IDs of those items, but all the data that we defined in the Pydantic *model* for reading items: `Item`. +
    + +
    -//// tab | Python 3.10+ +## Update the App with Multiple Models -```Python hl_lines="13-15 29-32" -{!> ../../docs_src/sql_databases/sql_app_py310/schemas.py!} -``` +Now let's **refactor** this app a bit to increase **security** and **versatility**. -//// +If you check the previous app, in the UI you can see that, up to now, it lets the client decide the `id` of the `Hero` to create. 😱 -//// tab | Python 3.9+ +We shouldn't let that happen, they could overwrite an `id` we already have assigned in the DB. Deciding the `id` should be done by the **backend** or the **database**, **not by the client**. -```Python hl_lines="15-17 31-34" -{!> ../../docs_src/sql_databases/sql_app_py39/schemas.py!} -``` +Additionally, we create a `secret_name` for the hero, but so far, we are returning it everywhere, that's not very **secret**... 😅 -//// +We'll fix these things by adding a few **extra models**. Here's where SQLModel will shine. ✨ -//// tab | Python 3.8+ +### Create Multiple Models -```Python hl_lines="15-17 31-34" -{!> ../../docs_src/sql_databases/sql_app/schemas.py!} -``` +In **SQLModel**, any model class that has `table=True` is a **table model**. -//// +And any model class that doesn't have `table=True` is a **data model**, these ones are actually just Pydantic models (with a couple of small extra features). 🤓 -/// tip +With SQLModel, we can use **inheritance** to **avoid duplicating** all the fields in all the cases. -Notice that the `User`, the Pydantic *model* that will be used when reading a user (returning it from the API) doesn't include the `password`. +#### `HeroBase` - the base class -/// +Let's start with a `HeroBase` model that has all the **fields that are shared** by all the models: -### Use Pydantic's `orm_mode` +* `name` +* `age` -Now, in the Pydantic *models* for reading, `Item` and `User`, add an internal `Config` class. +{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:9] hl[7:9] *} -This `Config` class is used to provide configurations to Pydantic. +#### `Hero` - the *table model* -In the `Config` class, set the attribute `orm_mode = True`. +Then let's create `Hero`, the actual *table model*, with the **extra fields** that are not always in the other models: -//// tab | Python 3.10+ +* `id` +* `secret_name` -```Python hl_lines="13 17-18 29 34-35" -{!> ../../docs_src/sql_databases/sql_app_py310/schemas.py!} -``` +Because `Hero` inherits form `HeroBase`, it **also** has the **fields** declared in `HeroBase`, so all the fields for `Hero` are: -//// +* `id` +* `name` +* `age` +* `secret_name` -//// tab | Python 3.9+ +{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:14] hl[12:14] *} -```Python hl_lines="15 19-20 31 36-37" -{!> ../../docs_src/sql_databases/sql_app_py39/schemas.py!} -``` +#### `HeroPublic` - the public *data model* -//// +Next, we create a `HeroPublic` model, this is the one that will be **returned** to the clients of the API. -//// tab | Python 3.8+ +It has the same fields as `HeroBase`, so it won't include `secret_name`. -```Python hl_lines="15 19-20 31 36-37" -{!> ../../docs_src/sql_databases/sql_app/schemas.py!} -``` +Finally, the identity of our heroes is protected! 🥷 -//// +It also re-declares `id: int`. By doing this, we are making a **contract** with the API clients, so that they can always expect the `id` to be there and to be an `int` (it will never be `None`). /// tip -Notice it's assigning a value with `=`, like: - -`orm_mode = True` +Having the return model ensure that a value is always available and always `int` (not `None`) is very useful for the API clients, they can write much simpler code having this certainty. -It doesn't use `:` as for the type declarations before. - -This is setting a config value, not declaring a type. +Also, **automatically generated clients** will have simpler interfaces, so that the developers communicating with your API can have a much better time working with your API. 😎 /// -Pydantic's `orm_mode` will tell the Pydantic *model* to read the data even if it is not a `dict`, but an ORM model (or any other arbitrary object with attributes). - -This way, instead of only trying to get the `id` value from a `dict`, as in: - -```Python -id = data["id"] -``` - -it will also try to get it from an attribute, as in: - -```Python -id = data.id -``` +All the fields in `HeroPublic` are the same as in `HeroBase`, with `id` declared as `int` (not `None`): -And with this, the Pydantic *model* is compatible with ORMs, and you can just declare it in the `response_model` argument in your *path operations*. +* `id` +* `name` +* `age` +* `secret_name` -You will be able to return a database model and it will read the data from it. +{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:18] hl[17:18] *} -#### Technical Details about ORM mode +#### `HeroCreate` - the *data model* to create a hero -SQLAlchemy and many others are by default "lazy loading". +Now we create a `HeroCreate` model, this is the one that will **validate** the data from the clients. -That means, for example, that they don't fetch the data for relationships from the database unless you try to access the attribute that would contain that data. +It has the same fields as `HeroBase`, and it also has `secret_name`. -For example, accessing the attribute `items`: - -```Python -current_user.items -``` - -would make SQLAlchemy go to the `items` table and get the items for this user, but not before. - -Without `orm_mode`, if you returned a SQLAlchemy model from your *path operation*, it wouldn't include the relationship data. - -Even if you declared those relationships in your Pydantic models. - -But with ORM mode, as Pydantic itself will try to access the data it needs from attributes (instead of assuming a `dict`), you can declare the specific data you want to return and it will be able to go and get it, even from ORMs. - -## CRUD utils - -Now let's see the file `sql_app/crud.py`. - -In this file we will have reusable functions to interact with the data in the database. - -**CRUD** comes from: **C**reate, **R**ead, **U**pdate, and **D**elete. - -...although in this example we are only creating and reading. - -### Read data - -Import `Session` from `sqlalchemy.orm`, this will allow you to declare the type of the `db` parameters and have better type checks and completion in your functions. - -Import `models` (the SQLAlchemy models) and `schemas` (the Pydantic *models* / schemas). - -Create utility functions to: - -* Read a single user by ID and by email. -* Read multiple users. -* Read multiple items. - -```Python hl_lines="1 3 6-7 10-11 14-15 27-28" -{!../../docs_src/sql_databases/sql_app/crud.py!} -``` +Now, when the clients **create a new hero**, they will send the `secret_name`, it will be stored in the database, but those secret names won't be returned in the API to the clients. /// tip -By creating functions that are only dedicated to interacting with the database (get a user or an item) independent of your *path operation function*, you can more easily reuse them in multiple parts and also add unit tests for them. - -/// +This is how you would handle **passwords**. Receive them, but don't return them in the API. -### Create data - -Now create utility functions to create data. - -The steps are: - -* Create a SQLAlchemy model *instance* with your data. -* `add` that instance object to your database session. -* `commit` the changes to the database (so that they are saved). -* `refresh` your instance (so that it contains any new data from the database, like the generated ID). - -```Python hl_lines="18-24 31-36" -{!../../docs_src/sql_databases/sql_app/crud.py!} -``` - -/// info - -In Pydantic v1 the method was called `.dict()`, it was deprecated (but still supported) in Pydantic v2, and renamed to `.model_dump()`. - -The examples here use `.dict()` for compatibility with Pydantic v1, but you should use `.model_dump()` instead if you can use Pydantic v2. +You would also **hash** the values of the passwords before storing them, **never store them in plain text**. /// -/// tip +The fields of `HeroCreate` are: -The SQLAlchemy model for `User` contains a `hashed_password` that should contain a secure hashed version of the password. +* `name` +* `age` +* `secret_name` -But as what the API client provides is the original password, you need to extract it and generate the hashed password in your application. +{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:22] hl[21:22] *} -And then pass the `hashed_password` argument with the value to save. +#### `HeroUpdate` - the *data model* to update a hero -/// +We didn't have a way to **update a hero** in the previous version of the app, but now with **multiple models**, we can do it. 🎉 -/// warning +The `HeroUpdate` *data model* is somewhat special, it has **all the same fields** that would be needed to create a new hero, but all the fields are **optional** (they all have a default value). This way, when you update a hero, you can send just the fields that you want to update. -This example is not secure, the password is not hashed. +Because all the **fields actually change** (the type now includes `None` and they now have a default value of `None`), we need to **re-declare** them. -In a real life application you would need to hash the password and never save them in plaintext. +We don't really need to inherit from `HeroBase` because we are re-declaring all the fields. I'll leave it inheriting just for consistency, but this is not necessary. It's more a matter of personal taste. 🤷 -For more details, go back to the Security section in the tutorial. +The fields of `HeroUpdate` are: -Here we are focusing only on the tools and mechanics of databases. +* `name` +* `age` +* `secret_name` -/// - -/// tip - -Instead of passing each of the keyword arguments to `Item` and reading each one of them from the Pydantic *model*, we are generating a `dict` with the Pydantic *model*'s data with: - -`item.dict()` +{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[7:28] hl[25:28] *} -and then we are passing the `dict`'s key-value pairs as the keyword arguments to the SQLAlchemy `Item`, with: +### Create with `HeroCreate` and return a `HeroPublic` -`Item(**item.dict())` - -And then we pass the extra keyword argument `owner_id` that is not provided by the Pydantic *model*, with: - -`Item(**item.dict(), owner_id=user_id)` - -/// +Now that we have **multiple models**, we can update the parts of the app that use them. -## Main **FastAPI** app +We receive in the request a `HeroCreate` *data model*, and from it, we create a `Hero` *table model*. -And now in the file `sql_app/main.py` let's integrate and use all the other parts we created before. +This new *table model* `Hero` will have the fields sent by the client, and will also have an `id` generated by the database. -### Create the database tables +Then we return the same *table model* `Hero` as is from the function. But as we declare the `response_model` with the `HeroPublic` *data model*, **FastAPI** will use `HeroPublic` to validate and serialize the data. -In a very simplistic way create the database tables: - -//// tab | Python 3.9+ - -```Python hl_lines="7" -{!> ../../docs_src/sql_databases/sql_app_py39/main.py!} -``` - -//// - -//// tab | Python 3.8+ - -```Python hl_lines="9" -{!> ../../docs_src/sql_databases/sql_app/main.py!} -``` - -//// - -#### Alembic Note - -Normally you would probably initialize your database (create tables, etc) with Alembic. - -And you would also use Alembic for "migrations" (that's its main job). - -A "migration" is the set of steps needed whenever you change the structure of your SQLAlchemy models, add a new attribute, etc. to replicate those changes in the database, add a new column, a new table, etc. - -You can find an example of Alembic in a FastAPI project in the [Full Stack FastAPI Template](../project-generation.md){.internal-link target=_blank}. Specifically in the `alembic` directory in the source code. - -### Create a dependency - -Now use the `SessionLocal` class we created in the `sql_app/database.py` file to create a dependency. - -We need to have an independent database session/connection (`SessionLocal`) per request, use the same session through all the request and then close it after the request is finished. - -And then a new session will be created for the next request. - -For that, we will create a new dependency with `yield`, as explained before in the section about [Dependencies with `yield`](dependencies/dependencies-with-yield.md){.internal-link target=_blank}. - -Our dependency will create a new SQLAlchemy `SessionLocal` that will be used in a single request, and then close it once the request is finished. - -//// tab | Python 3.9+ - -```Python hl_lines="13-18" -{!> ../../docs_src/sql_databases/sql_app_py39/main.py!} -``` - -//// - -//// tab | Python 3.8+ - -```Python hl_lines="15-20" -{!> ../../docs_src/sql_databases/sql_app/main.py!} -``` - -//// - -/// info - -We put the creation of the `SessionLocal()` and handling of the requests in a `try` block. - -And then we close it in the `finally` block. - -This way we make sure the database session is always closed after the request. Even if there was an exception while processing the request. - -But you can't raise another exception from the exit code (after `yield`). See more in [Dependencies with `yield` and `HTTPException`](dependencies/dependencies-with-yield.md#dependencies-with-yield-and-httpexception){.internal-link target=_blank} - -/// - -And then, when using the dependency in a *path operation function*, we declare it with the type `Session` we imported directly from SQLAlchemy. - -This will then give us better editor support inside the *path operation function*, because the editor will know that the `db` parameter is of type `Session`: - -//// tab | Python 3.9+ - -```Python hl_lines="22 30 36 45 51" -{!> ../../docs_src/sql_databases/sql_app_py39/main.py!} -``` - -//// - -//// tab | Python 3.8+ - -```Python hl_lines="24 32 38 47 53" -{!> ../../docs_src/sql_databases/sql_app/main.py!} -``` - -//// - -/// info | "Technical Details" - -The parameter `db` is actually of type `SessionLocal`, but this class (created with `sessionmaker()`) is a "proxy" of a SQLAlchemy `Session`, so, the editor doesn't really know what methods are provided. - -But by declaring the type as `Session`, the editor now can know the available methods (`.add()`, `.query()`, `.commit()`, etc) and can provide better support (like completion). The type declaration doesn't affect the actual object. - -/// - -### Create your **FastAPI** *path operations* - -Now, finally, here's the standard **FastAPI** *path operations* code. - -//// tab | Python 3.9+ - -```Python hl_lines="21-26 29-32 35-40 43-47 50-53" -{!> ../../docs_src/sql_databases/sql_app_py39/main.py!} -``` - -//// - -//// tab | Python 3.8+ - -```Python hl_lines="23-28 31-34 37-42 45-49 52-55" -{!> ../../docs_src/sql_databases/sql_app/main.py!} -``` - -//// - -We are creating the database session before each request in the dependency with `yield`, and then closing it afterwards. - -And then we can create the required dependency in the *path operation function*, to get that session directly. - -With that, we can just call `crud.get_user` directly from inside of the *path operation function* and use that session. - -/// tip - -Notice that the values you return are SQLAlchemy models, or lists of SQLAlchemy models. - -But as all the *path operations* have a `response_model` with Pydantic *models* / schemas using `orm_mode`, the data declared in your Pydantic models will be extracted from them and returned to the client, with all the normal filtering and validation. - -/// +{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[56:62] hl[56:58] *} /// tip -Also notice that there are `response_models` that have standard Python types like `List[schemas.Item]`. - -But as the content/parameter of that `List` is a Pydantic *model* with `orm_mode`, the data will be retrieved and returned to the client as normally, without problems. - -/// - -### About `def` vs `async def` - -Here we are using SQLAlchemy code inside of the *path operation function* and in the dependency, and, in turn, it will go and communicate with an external database. - -That could potentially require some "waiting". - -But as SQLAlchemy doesn't have compatibility for using `await` directly, as would be with something like: - -```Python -user = await db.query(User).first() -``` - -...and instead we are using: - -```Python -user = db.query(User).first() -``` - -Then we should declare the *path operation functions* and the dependency without `async def`, just with a normal `def`, as: - -```Python hl_lines="2" -@app.get("/users/{user_id}", response_model=schemas.User) -def read_user(user_id: int, db: Session = Depends(get_db)): - db_user = crud.get_user(db, user_id=user_id) - ... -``` - -/// info +Now we use `response_model=HeroPublic` instead of the **return type annotation** `-> HeroPublic` because the value that we are returning is actually *not* a `HeroPublic`. -If you need to connect to your relational database asynchronously, see [Async SQL (Relational) Databases](../how-to/async-sql-encode-databases.md){.internal-link target=_blank}. +If we had declared `-> HeroPublic`, your editor and linter would complain (rightfully so) that you are returning a `Hero` instead of a `HeroPublic`. -/// - -/// note | "Very Technical Details" - -If you are curious and have a deep technical knowledge, you can check the very technical details of how this `async def` vs `def` is handled in the [Async](../async.md#very-technical-details){.internal-link target=_blank} docs. +By declaring it in `response_model` we are telling **FastAPI** to do its thing, without interfering with the type annotations and the help from your editor and other tools. /// -## Migrations - -Because we are using SQLAlchemy directly and we don't require any kind of plug-in for it to work with **FastAPI**, we could integrate database migrations with Alembic directly. - -And as the code related to SQLAlchemy and the SQLAlchemy models lives in separate independent files, you would even be able to perform the migrations with Alembic without having to install FastAPI, Pydantic, or anything else. - -The same way, you would be able to use the same SQLAlchemy models and utilities in other parts of your code that are not related to **FastAPI**. - -For example, in a background task worker with Celery, RQ, or ARQ. - -## Review all the files - - Remember you should have a directory named `my_super_project` that contains a sub-directory called `sql_app`. - -`sql_app` should have the following files: - -* `sql_app/__init__.py`: is an empty file. - -* `sql_app/database.py`: - -```Python -{!../../docs_src/sql_databases/sql_app/database.py!} -``` - -* `sql_app/models.py`: +### Read Heroes with `HeroPublic` -```Python -{!../../docs_src/sql_databases/sql_app/models.py!} -``` - -* `sql_app/schemas.py`: - -//// tab | Python 3.10+ - -```Python -{!> ../../docs_src/sql_databases/sql_app_py310/schemas.py!} -``` - -//// - -//// tab | Python 3.9+ - -```Python -{!> ../../docs_src/sql_databases/sql_app_py39/schemas.py!} -``` - -//// +We can do the same as before to **read** `Hero`s, again, we use `response_model=list[HeroPublic]` to ensure that the data is validated and serialized correctly. -//// tab | Python 3.8+ +{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[65:72] hl[65] *} -```Python -{!> ../../docs_src/sql_databases/sql_app/schemas.py!} -``` - -//// - -* `sql_app/crud.py`: - -```Python -{!../../docs_src/sql_databases/sql_app/crud.py!} -``` +### Read One Hero with `HeroPublic` -* `sql_app/main.py`: +We can **read** a single hero: -//// tab | Python 3.9+ +{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[75:80] hl[77] *} -```Python -{!> ../../docs_src/sql_databases/sql_app_py39/main.py!} -``` +### Update a Hero with `HeroUpdate` -//// +We can **update a hero**. For this we use an HTTP `PATCH` operation. -//// tab | Python 3.8+ +And in the code, we get a `dict` with all the data sent by the client, **only the data sent by the client**, excluding any values that would be there just for being the default values. To do it we use `exclude_unset=True`. This is the main trick. 🪄 -```Python -{!> ../../docs_src/sql_databases/sql_app/main.py!} -``` +Then we use `hero_db.sqlmodel_update(hero_data)` to update the `hero_db` with the data from `hero_data`. -//// +{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[83:93] hl[83:84,88:89] *} -## Check it +### Delete a Hero Again -You can copy this code and use it as is. +**Deleting** a hero stays pretty much the same. -/// info +We won't satisfy the desire to refactor everything in this one. 😅 -In fact, the code shown here is part of the tests. As most of the code in these docs. +{* ../../docs_src/sql_databases/tutorial002_an_py310.py ln[96:103] hl[101] *} -/// - -Then you can run it with Uvicorn: +### Run the App Again +You can run the app again:
    ```console -$ uvicorn sql_app.main:app --reload +$ fastapi dev main.py INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) ```
    -And then, you can open your browser at http://127.0.0.1:8000/docs. - -And you will be able to interact with your **FastAPI** application, reading data from a real database: - - - -## Interact with the database directly - -If you want to explore the SQLite database (file) directly, independently of FastAPI, to debug its contents, add tables, columns, records, modify data, etc. you can use DB Browser for SQLite. - -It will look like this: +If you go to the `/docs` API UI, you will see that it is now updated, and it won't expect to receive the `id` from the client when creating a hero, etc. +
    +
    -You can also use an online SQLite browser like SQLite Viewer or ExtendsClass. - -## Alternative DB session with middleware - -If you can't use dependencies with `yield` -- for example, if you are not using **Python 3.7** and can't install the "backports" mentioned above for **Python 3.6** -- you can set up the session in a "middleware" in a similar way. - -A "middleware" is basically a function that is always executed for each request, with some code executed before, and some code executed after the endpoint function. - -### Create a middleware - -The middleware we'll add (just a function) will create a new SQLAlchemy `SessionLocal` for each request, add it to the request and then close it once the request is finished. - -//// tab | Python 3.9+ - -```Python hl_lines="12-20" -{!> ../../docs_src/sql_databases/sql_app_py39/alt_main.py!} -``` - -//// - -//// tab | Python 3.8+ - -```Python hl_lines="14-22" -{!> ../../docs_src/sql_databases/sql_app/alt_main.py!} -``` - -//// - -/// info - -We put the creation of the `SessionLocal()` and handling of the requests in a `try` block. - -And then we close it in the `finally` block. - -This way we make sure the database session is always closed after the request. Even if there was an exception while processing the request. - -/// - -### About `request.state` - -`request.state` is a property of each `Request` object. It is there to store arbitrary objects attached to the request itself, like the database session in this case. You can read more about it in Starlette's docs about `Request` state. - -For us in this case, it helps us ensure a single database session is used through all the request, and then closed afterwards (in the middleware). - -### Dependencies with `yield` or middleware - -Adding a **middleware** here is similar to what a dependency with `yield` does, with some differences: - -* It requires more code and is a bit more complex. -* The middleware has to be an `async` function. - * If there is code in it that has to "wait" for the network, it could "block" your application there and degrade performance a bit. - * Although it's probably not very problematic here with the way `SQLAlchemy` works. - * But if you added more code to the middleware that had a lot of I/O waiting, it could then be problematic. -* A middleware is run for *every* request. - * So, a connection will be created for every request. - * Even when the *path operation* that handles that request didn't need the DB. - -/// tip - -It's probably better to use dependencies with `yield` when they are enough for the use case. - -/// - -/// info - -Dependencies with `yield` were added recently to **FastAPI**. +## Recap -A previous version of this tutorial only had the examples with a middleware and there are probably several applications using the middleware for database session management. +You can use **SQLModel** to interact with a SQL database and simplify the code with *data models* and *table models*. -/// +You can learn a lot more at the **SQLModel** docs, there's a longer mini tutorial on using SQLModel with **FastAPI**. 🚀 diff --git a/docs/en/mkdocs.yml b/docs/en/mkdocs.yml index e55c6f176b..8e0f6765d1 100644 --- a/docs/en/mkdocs.yml +++ b/docs/en/mkdocs.yml @@ -71,13 +71,11 @@ plugins: redirects: redirect_maps: deployment/deta.md: deployment/cloud.md - advanced/sql-databases-peewee.md: how-to/sql-databases-peewee.md - advanced/async-sql-databases.md: how-to/async-sql-encode-databases.md - advanced/nosql-databases.md: how-to/nosql-databases-couchbase.md advanced/graphql.md: how-to/graphql.md advanced/custom-request-and-route.md: how-to/custom-request-and-route.md advanced/conditional-openapi.md: how-to/conditional-openapi.md advanced/extending-openapi.md: how-to/extending-openapi.md + advanced/testing-database.md: how-to/testing-database.md mkdocstrings: handlers: python: @@ -187,7 +185,6 @@ nav: - advanced/testing-websockets.md - advanced/testing-events.md - advanced/testing-dependencies.md - - advanced/testing-database.md - advanced/async-tests.md - advanced/settings.md - advanced/openapi-callbacks.md @@ -214,9 +211,7 @@ nav: - how-to/separate-openapi-schemas.md - how-to/custom-docs-ui-assets.md - how-to/configure-swagger-ui.md - - how-to/sql-databases-peewee.md - - how-to/async-sql-encode-databases.md - - how-to/nosql-databases-couchbase.md + - how-to/testing-database.md - Reference (Code API): - reference/index.md - reference/fastapi.md diff --git a/docs/zh/docs/advanced/testing-database.md b/docs/zh/docs/advanced/testing-database.md deleted file mode 100644 index ecab4f65b2..0000000000 --- a/docs/zh/docs/advanced/testing-database.md +++ /dev/null @@ -1,101 +0,0 @@ -# 测试数据库 - -您还可以使用[测试依赖项](testing-dependencies.md){.internal-link target=_blank}中的覆盖依赖项方法变更测试的数据库。 - -实现设置其它测试数据库、在测试后回滚数据、或预填测试数据等操作。 - -本章的主要思路与上一章完全相同。 - -## 为 SQL 应用添加测试 - -为了使用测试数据库,我们要升级 [SQL 关系型数据库](../tutorial/sql-databases.md){.internal-link target=_blank} 一章中的示例。 - -应用的所有代码都一样,直接查看那一章的示例代码即可。 - -本章只是新添加了测试文件。 - -正常的依赖项 `get_db()` 返回数据库会话。 - -测试时使用覆盖依赖项返回自定义数据库会话代替正常的依赖项。 - -本例中,要创建仅用于测试的临时数据库。 - -## 文件架构 - -创建新文件 `sql_app/tests/test_sql_app.py`。 - -因此,新的文件架构如下: - -``` hl_lines="9-11" -. -└── sql_app - ├── __init__.py - ├── crud.py - ├── database.py - ├── main.py - ├── models.py - ├── schemas.py - └── tests - ├── __init__.py - └── test_sql_app.py -``` - -## 创建新的数据库会话 - -首先,为新建数据库创建新的数据库会话。 - -测试时,使用 `test.db` 替代 `sql_app.db`。 - -但其余的会话代码基本上都是一样的,只要复制就可以了。 - -```Python hl_lines="8-13" -{!../../docs_src/sql_databases/sql_app/tests/test_sql_app.py!} -``` - -/// tip | "提示" - -为减少代码重复,最好把这段代码写成函数,在 `database.py` 与 `tests/test_sql_app.py`中使用。 - -为了把注意力集中在测试代码上,本例只是复制了这段代码。 - -/// - -## 创建数据库 - -因为现在是想在新文件中使用新数据库,所以要使用以下代码创建数据库: - -```Python -Base.metadata.create_all(bind=engine) -``` - -一般是在 `main.py` 中调用这行代码,但在 `main.py` 里,这行代码用于创建 `sql_app.db`,但是现在要为测试创建 `test.db`。 - -因此,要在测试代码中添加这行代码创建新的数据库文件。 - -```Python hl_lines="16" -{!../../docs_src/sql_databases/sql_app/tests/test_sql_app.py!} -``` - -## 覆盖依赖项 - -接下来,创建覆盖依赖项,并为应用添加覆盖内容。 - -```Python hl_lines="19-24 27" -{!../../docs_src/sql_databases/sql_app/tests/test_sql_app.py!} -``` - -/// tip | "提示" - -`overrider_get_db()` 与 `get_db` 的代码几乎完全一样,只是 `overrider_get_db` 中使用测试数据库的 `TestingSessionLocal`。 - -/// - -## 测试应用 - -然后,就可以正常测试了。 - -```Python hl_lines="32-47" -{!../../docs_src/sql_databases/sql_app/tests/test_sql_app.py!} -``` - -测试期间,所有在数据库中所做的修改都在 `test.db` 里,不会影响主应用的 `sql_app.db`。 diff --git a/docs_src/async_sql_databases/tutorial001.py b/docs_src/async_sql_databases/tutorial001.py deleted file mode 100644 index cbf43d790f..0000000000 --- a/docs_src/async_sql_databases/tutorial001.py +++ /dev/null @@ -1,65 +0,0 @@ -from typing import List - -import databases -import sqlalchemy -from fastapi import FastAPI -from pydantic import BaseModel - -# SQLAlchemy specific code, as with any other app -DATABASE_URL = "sqlite:///./test.db" -# DATABASE_URL = "postgresql://user:password@postgresserver/db" - -database = databases.Database(DATABASE_URL) - -metadata = sqlalchemy.MetaData() - -notes = sqlalchemy.Table( - "notes", - metadata, - sqlalchemy.Column("id", sqlalchemy.Integer, primary_key=True), - sqlalchemy.Column("text", sqlalchemy.String), - sqlalchemy.Column("completed", sqlalchemy.Boolean), -) - - -engine = sqlalchemy.create_engine( - DATABASE_URL, connect_args={"check_same_thread": False} -) -metadata.create_all(engine) - - -class NoteIn(BaseModel): - text: str - completed: bool - - -class Note(BaseModel): - id: int - text: str - completed: bool - - -app = FastAPI() - - -@app.on_event("startup") -async def startup(): - await database.connect() - - -@app.on_event("shutdown") -async def shutdown(): - await database.disconnect() - - -@app.get("/notes/", response_model=List[Note]) -async def read_notes(): - query = notes.select() - return await database.fetch_all(query) - - -@app.post("/notes/", response_model=Note) -async def create_note(note: NoteIn): - query = notes.insert().values(text=note.text, completed=note.completed) - last_record_id = await database.execute(query) - return {**note.dict(), "id": last_record_id} diff --git a/docs_src/nosql_databases/tutorial001.py b/docs_src/nosql_databases/tutorial001.py deleted file mode 100644 index 91893e5281..0000000000 --- a/docs_src/nosql_databases/tutorial001.py +++ /dev/null @@ -1,53 +0,0 @@ -from typing import Union - -from couchbase import LOCKMODE_WAIT -from couchbase.bucket import Bucket -from couchbase.cluster import Cluster, PasswordAuthenticator -from fastapi import FastAPI -from pydantic import BaseModel - -USERPROFILE_DOC_TYPE = "userprofile" - - -def get_bucket(): - cluster = Cluster( - "couchbase://couchbasehost:8091?fetch_mutation_tokens=1&operation_timeout=30&n1ql_timeout=300" - ) - authenticator = PasswordAuthenticator("username", "password") - cluster.authenticate(authenticator) - bucket: Bucket = cluster.open_bucket("bucket_name", lockmode=LOCKMODE_WAIT) - bucket.timeout = 30 - bucket.n1ql_timeout = 300 - return bucket - - -class User(BaseModel): - username: str - email: Union[str, None] = None - full_name: Union[str, None] = None - disabled: Union[bool, None] = None - - -class UserInDB(User): - type: str = USERPROFILE_DOC_TYPE - hashed_password: str - - -def get_user(bucket: Bucket, username: str): - doc_id = f"userprofile::{username}" - result = bucket.get(doc_id, quiet=True) - if not result.value: - return None - user = UserInDB(**result.value) - return user - - -# FastAPI specific code -app = FastAPI() - - -@app.get("/users/{username}", response_model=User) -def read_user(username: str): - bucket = get_bucket() - user = get_user(bucket=bucket, username=username) - return user diff --git a/docs_src/sql_databases/sql_app/__init__.py b/docs_src/sql_databases/sql_app/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/docs_src/sql_databases/sql_app/alt_main.py b/docs_src/sql_databases/sql_app/alt_main.py deleted file mode 100644 index f7206bcb41..0000000000 --- a/docs_src/sql_databases/sql_app/alt_main.py +++ /dev/null @@ -1,62 +0,0 @@ -from typing import List - -from fastapi import Depends, FastAPI, HTTPException, Request, Response -from sqlalchemy.orm import Session - -from . import crud, models, schemas -from .database import SessionLocal, engine - -models.Base.metadata.create_all(bind=engine) - -app = FastAPI() - - -@app.middleware("http") -async def db_session_middleware(request: Request, call_next): - response = Response("Internal server error", status_code=500) - try: - request.state.db = SessionLocal() - response = await call_next(request) - finally: - request.state.db.close() - return response - - -# Dependency -def get_db(request: Request): - return request.state.db - - -@app.post("/users/", response_model=schemas.User) -def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)): - db_user = crud.get_user_by_email(db, email=user.email) - if db_user: - raise HTTPException(status_code=400, detail="Email already registered") - return crud.create_user(db=db, user=user) - - -@app.get("/users/", response_model=List[schemas.User]) -def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): - users = crud.get_users(db, skip=skip, limit=limit) - return users - - -@app.get("/users/{user_id}", response_model=schemas.User) -def read_user(user_id: int, db: Session = Depends(get_db)): - db_user = crud.get_user(db, user_id=user_id) - if db_user is None: - raise HTTPException(status_code=404, detail="User not found") - return db_user - - -@app.post("/users/{user_id}/items/", response_model=schemas.Item) -def create_item_for_user( - user_id: int, item: schemas.ItemCreate, db: Session = Depends(get_db) -): - return crud.create_user_item(db=db, item=item, user_id=user_id) - - -@app.get("/items/", response_model=List[schemas.Item]) -def read_items(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): - items = crud.get_items(db, skip=skip, limit=limit) - return items diff --git a/docs_src/sql_databases/sql_app/crud.py b/docs_src/sql_databases/sql_app/crud.py deleted file mode 100644 index 679acdb5ce..0000000000 --- a/docs_src/sql_databases/sql_app/crud.py +++ /dev/null @@ -1,36 +0,0 @@ -from sqlalchemy.orm import Session - -from . import models, schemas - - -def get_user(db: Session, user_id: int): - return db.query(models.User).filter(models.User.id == user_id).first() - - -def get_user_by_email(db: Session, email: str): - return db.query(models.User).filter(models.User.email == email).first() - - -def get_users(db: Session, skip: int = 0, limit: int = 100): - return db.query(models.User).offset(skip).limit(limit).all() - - -def create_user(db: Session, user: schemas.UserCreate): - fake_hashed_password = user.password + "notreallyhashed" - db_user = models.User(email=user.email, hashed_password=fake_hashed_password) - db.add(db_user) - db.commit() - db.refresh(db_user) - return db_user - - -def get_items(db: Session, skip: int = 0, limit: int = 100): - return db.query(models.Item).offset(skip).limit(limit).all() - - -def create_user_item(db: Session, item: schemas.ItemCreate, user_id: int): - db_item = models.Item(**item.dict(), owner_id=user_id) - db.add(db_item) - db.commit() - db.refresh(db_item) - return db_item diff --git a/docs_src/sql_databases/sql_app/database.py b/docs_src/sql_databases/sql_app/database.py deleted file mode 100644 index 45a8b9f694..0000000000 --- a/docs_src/sql_databases/sql_app/database.py +++ /dev/null @@ -1,13 +0,0 @@ -from sqlalchemy import create_engine -from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import sessionmaker - -SQLALCHEMY_DATABASE_URL = "sqlite:///./sql_app.db" -# SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db" - -engine = create_engine( - SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False} -) -SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) - -Base = declarative_base() diff --git a/docs_src/sql_databases/sql_app/main.py b/docs_src/sql_databases/sql_app/main.py deleted file mode 100644 index e7508c59d4..0000000000 --- a/docs_src/sql_databases/sql_app/main.py +++ /dev/null @@ -1,55 +0,0 @@ -from typing import List - -from fastapi import Depends, FastAPI, HTTPException -from sqlalchemy.orm import Session - -from . import crud, models, schemas -from .database import SessionLocal, engine - -models.Base.metadata.create_all(bind=engine) - -app = FastAPI() - - -# Dependency -def get_db(): - db = SessionLocal() - try: - yield db - finally: - db.close() - - -@app.post("/users/", response_model=schemas.User) -def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)): - db_user = crud.get_user_by_email(db, email=user.email) - if db_user: - raise HTTPException(status_code=400, detail="Email already registered") - return crud.create_user(db=db, user=user) - - -@app.get("/users/", response_model=List[schemas.User]) -def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): - users = crud.get_users(db, skip=skip, limit=limit) - return users - - -@app.get("/users/{user_id}", response_model=schemas.User) -def read_user(user_id: int, db: Session = Depends(get_db)): - db_user = crud.get_user(db, user_id=user_id) - if db_user is None: - raise HTTPException(status_code=404, detail="User not found") - return db_user - - -@app.post("/users/{user_id}/items/", response_model=schemas.Item) -def create_item_for_user( - user_id: int, item: schemas.ItemCreate, db: Session = Depends(get_db) -): - return crud.create_user_item(db=db, item=item, user_id=user_id) - - -@app.get("/items/", response_model=List[schemas.Item]) -def read_items(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): - items = crud.get_items(db, skip=skip, limit=limit) - return items diff --git a/docs_src/sql_databases/sql_app/models.py b/docs_src/sql_databases/sql_app/models.py deleted file mode 100644 index 09ae2a8077..0000000000 --- a/docs_src/sql_databases/sql_app/models.py +++ /dev/null @@ -1,26 +0,0 @@ -from sqlalchemy import Boolean, Column, ForeignKey, Integer, String -from sqlalchemy.orm import relationship - -from .database import Base - - -class User(Base): - __tablename__ = "users" - - id = Column(Integer, primary_key=True) - email = Column(String, unique=True, index=True) - hashed_password = Column(String) - is_active = Column(Boolean, default=True) - - items = relationship("Item", back_populates="owner") - - -class Item(Base): - __tablename__ = "items" - - id = Column(Integer, primary_key=True) - title = Column(String, index=True) - description = Column(String, index=True) - owner_id = Column(Integer, ForeignKey("users.id")) - - owner = relationship("User", back_populates="items") diff --git a/docs_src/sql_databases/sql_app/schemas.py b/docs_src/sql_databases/sql_app/schemas.py deleted file mode 100644 index c49beba882..0000000000 --- a/docs_src/sql_databases/sql_app/schemas.py +++ /dev/null @@ -1,37 +0,0 @@ -from typing import List, Union - -from pydantic import BaseModel - - -class ItemBase(BaseModel): - title: str - description: Union[str, None] = None - - -class ItemCreate(ItemBase): - pass - - -class Item(ItemBase): - id: int - owner_id: int - - class Config: - orm_mode = True - - -class UserBase(BaseModel): - email: str - - -class UserCreate(UserBase): - password: str - - -class User(UserBase): - id: int - is_active: bool - items: List[Item] = [] - - class Config: - orm_mode = True diff --git a/docs_src/sql_databases/sql_app/tests/__init__.py b/docs_src/sql_databases/sql_app/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/docs_src/sql_databases/sql_app/tests/test_sql_app.py b/docs_src/sql_databases/sql_app/tests/test_sql_app.py deleted file mode 100644 index 5f55add0a9..0000000000 --- a/docs_src/sql_databases/sql_app/tests/test_sql_app.py +++ /dev/null @@ -1,50 +0,0 @@ -from fastapi.testclient import TestClient -from sqlalchemy import create_engine -from sqlalchemy.orm import sessionmaker -from sqlalchemy.pool import StaticPool - -from ..database import Base -from ..main import app, get_db - -SQLALCHEMY_DATABASE_URL = "sqlite://" - -engine = create_engine( - SQLALCHEMY_DATABASE_URL, - connect_args={"check_same_thread": False}, - poolclass=StaticPool, -) -TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) - - -Base.metadata.create_all(bind=engine) - - -def override_get_db(): - try: - db = TestingSessionLocal() - yield db - finally: - db.close() - - -app.dependency_overrides[get_db] = override_get_db - -client = TestClient(app) - - -def test_create_user(): - response = client.post( - "/users/", - json={"email": "deadpool@example.com", "password": "chimichangas4life"}, - ) - assert response.status_code == 200, response.text - data = response.json() - assert data["email"] == "deadpool@example.com" - assert "id" in data - user_id = data["id"] - - response = client.get(f"/users/{user_id}") - assert response.status_code == 200, response.text - data = response.json() - assert data["email"] == "deadpool@example.com" - assert data["id"] == user_id diff --git a/docs_src/sql_databases/sql_app_py310/__init__.py b/docs_src/sql_databases/sql_app_py310/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/docs_src/sql_databases/sql_app_py310/alt_main.py b/docs_src/sql_databases/sql_app_py310/alt_main.py deleted file mode 100644 index 5de88ec3a1..0000000000 --- a/docs_src/sql_databases/sql_app_py310/alt_main.py +++ /dev/null @@ -1,60 +0,0 @@ -from fastapi import Depends, FastAPI, HTTPException, Request, Response -from sqlalchemy.orm import Session - -from . import crud, models, schemas -from .database import SessionLocal, engine - -models.Base.metadata.create_all(bind=engine) - -app = FastAPI() - - -@app.middleware("http") -async def db_session_middleware(request: Request, call_next): - response = Response("Internal server error", status_code=500) - try: - request.state.db = SessionLocal() - response = await call_next(request) - finally: - request.state.db.close() - return response - - -# Dependency -def get_db(request: Request): - return request.state.db - - -@app.post("/users/", response_model=schemas.User) -def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)): - db_user = crud.get_user_by_email(db, email=user.email) - if db_user: - raise HTTPException(status_code=400, detail="Email already registered") - return crud.create_user(db=db, user=user) - - -@app.get("/users/", response_model=list[schemas.User]) -def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): - users = crud.get_users(db, skip=skip, limit=limit) - return users - - -@app.get("/users/{user_id}", response_model=schemas.User) -def read_user(user_id: int, db: Session = Depends(get_db)): - db_user = crud.get_user(db, user_id=user_id) - if db_user is None: - raise HTTPException(status_code=404, detail="User not found") - return db_user - - -@app.post("/users/{user_id}/items/", response_model=schemas.Item) -def create_item_for_user( - user_id: int, item: schemas.ItemCreate, db: Session = Depends(get_db) -): - return crud.create_user_item(db=db, item=item, user_id=user_id) - - -@app.get("/items/", response_model=list[schemas.Item]) -def read_items(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): - items = crud.get_items(db, skip=skip, limit=limit) - return items diff --git a/docs_src/sql_databases/sql_app_py310/crud.py b/docs_src/sql_databases/sql_app_py310/crud.py deleted file mode 100644 index 679acdb5ce..0000000000 --- a/docs_src/sql_databases/sql_app_py310/crud.py +++ /dev/null @@ -1,36 +0,0 @@ -from sqlalchemy.orm import Session - -from . import models, schemas - - -def get_user(db: Session, user_id: int): - return db.query(models.User).filter(models.User.id == user_id).first() - - -def get_user_by_email(db: Session, email: str): - return db.query(models.User).filter(models.User.email == email).first() - - -def get_users(db: Session, skip: int = 0, limit: int = 100): - return db.query(models.User).offset(skip).limit(limit).all() - - -def create_user(db: Session, user: schemas.UserCreate): - fake_hashed_password = user.password + "notreallyhashed" - db_user = models.User(email=user.email, hashed_password=fake_hashed_password) - db.add(db_user) - db.commit() - db.refresh(db_user) - return db_user - - -def get_items(db: Session, skip: int = 0, limit: int = 100): - return db.query(models.Item).offset(skip).limit(limit).all() - - -def create_user_item(db: Session, item: schemas.ItemCreate, user_id: int): - db_item = models.Item(**item.dict(), owner_id=user_id) - db.add(db_item) - db.commit() - db.refresh(db_item) - return db_item diff --git a/docs_src/sql_databases/sql_app_py310/database.py b/docs_src/sql_databases/sql_app_py310/database.py deleted file mode 100644 index 45a8b9f694..0000000000 --- a/docs_src/sql_databases/sql_app_py310/database.py +++ /dev/null @@ -1,13 +0,0 @@ -from sqlalchemy import create_engine -from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import sessionmaker - -SQLALCHEMY_DATABASE_URL = "sqlite:///./sql_app.db" -# SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db" - -engine = create_engine( - SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False} -) -SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) - -Base = declarative_base() diff --git a/docs_src/sql_databases/sql_app_py310/main.py b/docs_src/sql_databases/sql_app_py310/main.py deleted file mode 100644 index a9856d0b68..0000000000 --- a/docs_src/sql_databases/sql_app_py310/main.py +++ /dev/null @@ -1,53 +0,0 @@ -from fastapi import Depends, FastAPI, HTTPException -from sqlalchemy.orm import Session - -from . import crud, models, schemas -from .database import SessionLocal, engine - -models.Base.metadata.create_all(bind=engine) - -app = FastAPI() - - -# Dependency -def get_db(): - db = SessionLocal() - try: - yield db - finally: - db.close() - - -@app.post("/users/", response_model=schemas.User) -def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)): - db_user = crud.get_user_by_email(db, email=user.email) - if db_user: - raise HTTPException(status_code=400, detail="Email already registered") - return crud.create_user(db=db, user=user) - - -@app.get("/users/", response_model=list[schemas.User]) -def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): - users = crud.get_users(db, skip=skip, limit=limit) - return users - - -@app.get("/users/{user_id}", response_model=schemas.User) -def read_user(user_id: int, db: Session = Depends(get_db)): - db_user = crud.get_user(db, user_id=user_id) - if db_user is None: - raise HTTPException(status_code=404, detail="User not found") - return db_user - - -@app.post("/users/{user_id}/items/", response_model=schemas.Item) -def create_item_for_user( - user_id: int, item: schemas.ItemCreate, db: Session = Depends(get_db) -): - return crud.create_user_item(db=db, item=item, user_id=user_id) - - -@app.get("/items/", response_model=list[schemas.Item]) -def read_items(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): - items = crud.get_items(db, skip=skip, limit=limit) - return items diff --git a/docs_src/sql_databases/sql_app_py310/models.py b/docs_src/sql_databases/sql_app_py310/models.py deleted file mode 100644 index 09ae2a8077..0000000000 --- a/docs_src/sql_databases/sql_app_py310/models.py +++ /dev/null @@ -1,26 +0,0 @@ -from sqlalchemy import Boolean, Column, ForeignKey, Integer, String -from sqlalchemy.orm import relationship - -from .database import Base - - -class User(Base): - __tablename__ = "users" - - id = Column(Integer, primary_key=True) - email = Column(String, unique=True, index=True) - hashed_password = Column(String) - is_active = Column(Boolean, default=True) - - items = relationship("Item", back_populates="owner") - - -class Item(Base): - __tablename__ = "items" - - id = Column(Integer, primary_key=True) - title = Column(String, index=True) - description = Column(String, index=True) - owner_id = Column(Integer, ForeignKey("users.id")) - - owner = relationship("User", back_populates="items") diff --git a/docs_src/sql_databases/sql_app_py310/schemas.py b/docs_src/sql_databases/sql_app_py310/schemas.py deleted file mode 100644 index aea2e3f101..0000000000 --- a/docs_src/sql_databases/sql_app_py310/schemas.py +++ /dev/null @@ -1,35 +0,0 @@ -from pydantic import BaseModel - - -class ItemBase(BaseModel): - title: str - description: str | None = None - - -class ItemCreate(ItemBase): - pass - - -class Item(ItemBase): - id: int - owner_id: int - - class Config: - orm_mode = True - - -class UserBase(BaseModel): - email: str - - -class UserCreate(UserBase): - password: str - - -class User(UserBase): - id: int - is_active: bool - items: list[Item] = [] - - class Config: - orm_mode = True diff --git a/docs_src/sql_databases/sql_app_py310/tests/__init__.py b/docs_src/sql_databases/sql_app_py310/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/docs_src/sql_databases/sql_app_py310/tests/test_sql_app.py b/docs_src/sql_databases/sql_app_py310/tests/test_sql_app.py deleted file mode 100644 index c60c3356f8..0000000000 --- a/docs_src/sql_databases/sql_app_py310/tests/test_sql_app.py +++ /dev/null @@ -1,47 +0,0 @@ -from fastapi.testclient import TestClient -from sqlalchemy import create_engine -from sqlalchemy.orm import sessionmaker - -from ..database import Base -from ..main import app, get_db - -SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db" - -engine = create_engine( - SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False} -) -TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) - - -Base.metadata.create_all(bind=engine) - - -def override_get_db(): - try: - db = TestingSessionLocal() - yield db - finally: - db.close() - - -app.dependency_overrides[get_db] = override_get_db - -client = TestClient(app) - - -def test_create_user(): - response = client.post( - "/users/", - json={"email": "deadpool@example.com", "password": "chimichangas4life"}, - ) - assert response.status_code == 200, response.text - data = response.json() - assert data["email"] == "deadpool@example.com" - assert "id" in data - user_id = data["id"] - - response = client.get(f"/users/{user_id}") - assert response.status_code == 200, response.text - data = response.json() - assert data["email"] == "deadpool@example.com" - assert data["id"] == user_id diff --git a/docs_src/sql_databases/sql_app_py39/__init__.py b/docs_src/sql_databases/sql_app_py39/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/docs_src/sql_databases/sql_app_py39/alt_main.py b/docs_src/sql_databases/sql_app_py39/alt_main.py deleted file mode 100644 index 5de88ec3a1..0000000000 --- a/docs_src/sql_databases/sql_app_py39/alt_main.py +++ /dev/null @@ -1,60 +0,0 @@ -from fastapi import Depends, FastAPI, HTTPException, Request, Response -from sqlalchemy.orm import Session - -from . import crud, models, schemas -from .database import SessionLocal, engine - -models.Base.metadata.create_all(bind=engine) - -app = FastAPI() - - -@app.middleware("http") -async def db_session_middleware(request: Request, call_next): - response = Response("Internal server error", status_code=500) - try: - request.state.db = SessionLocal() - response = await call_next(request) - finally: - request.state.db.close() - return response - - -# Dependency -def get_db(request: Request): - return request.state.db - - -@app.post("/users/", response_model=schemas.User) -def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)): - db_user = crud.get_user_by_email(db, email=user.email) - if db_user: - raise HTTPException(status_code=400, detail="Email already registered") - return crud.create_user(db=db, user=user) - - -@app.get("/users/", response_model=list[schemas.User]) -def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): - users = crud.get_users(db, skip=skip, limit=limit) - return users - - -@app.get("/users/{user_id}", response_model=schemas.User) -def read_user(user_id: int, db: Session = Depends(get_db)): - db_user = crud.get_user(db, user_id=user_id) - if db_user is None: - raise HTTPException(status_code=404, detail="User not found") - return db_user - - -@app.post("/users/{user_id}/items/", response_model=schemas.Item) -def create_item_for_user( - user_id: int, item: schemas.ItemCreate, db: Session = Depends(get_db) -): - return crud.create_user_item(db=db, item=item, user_id=user_id) - - -@app.get("/items/", response_model=list[schemas.Item]) -def read_items(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): - items = crud.get_items(db, skip=skip, limit=limit) - return items diff --git a/docs_src/sql_databases/sql_app_py39/crud.py b/docs_src/sql_databases/sql_app_py39/crud.py deleted file mode 100644 index 679acdb5ce..0000000000 --- a/docs_src/sql_databases/sql_app_py39/crud.py +++ /dev/null @@ -1,36 +0,0 @@ -from sqlalchemy.orm import Session - -from . import models, schemas - - -def get_user(db: Session, user_id: int): - return db.query(models.User).filter(models.User.id == user_id).first() - - -def get_user_by_email(db: Session, email: str): - return db.query(models.User).filter(models.User.email == email).first() - - -def get_users(db: Session, skip: int = 0, limit: int = 100): - return db.query(models.User).offset(skip).limit(limit).all() - - -def create_user(db: Session, user: schemas.UserCreate): - fake_hashed_password = user.password + "notreallyhashed" - db_user = models.User(email=user.email, hashed_password=fake_hashed_password) - db.add(db_user) - db.commit() - db.refresh(db_user) - return db_user - - -def get_items(db: Session, skip: int = 0, limit: int = 100): - return db.query(models.Item).offset(skip).limit(limit).all() - - -def create_user_item(db: Session, item: schemas.ItemCreate, user_id: int): - db_item = models.Item(**item.dict(), owner_id=user_id) - db.add(db_item) - db.commit() - db.refresh(db_item) - return db_item diff --git a/docs_src/sql_databases/sql_app_py39/database.py b/docs_src/sql_databases/sql_app_py39/database.py deleted file mode 100644 index 45a8b9f694..0000000000 --- a/docs_src/sql_databases/sql_app_py39/database.py +++ /dev/null @@ -1,13 +0,0 @@ -from sqlalchemy import create_engine -from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import sessionmaker - -SQLALCHEMY_DATABASE_URL = "sqlite:///./sql_app.db" -# SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db" - -engine = create_engine( - SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False} -) -SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) - -Base = declarative_base() diff --git a/docs_src/sql_databases/sql_app_py39/main.py b/docs_src/sql_databases/sql_app_py39/main.py deleted file mode 100644 index a9856d0b68..0000000000 --- a/docs_src/sql_databases/sql_app_py39/main.py +++ /dev/null @@ -1,53 +0,0 @@ -from fastapi import Depends, FastAPI, HTTPException -from sqlalchemy.orm import Session - -from . import crud, models, schemas -from .database import SessionLocal, engine - -models.Base.metadata.create_all(bind=engine) - -app = FastAPI() - - -# Dependency -def get_db(): - db = SessionLocal() - try: - yield db - finally: - db.close() - - -@app.post("/users/", response_model=schemas.User) -def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)): - db_user = crud.get_user_by_email(db, email=user.email) - if db_user: - raise HTTPException(status_code=400, detail="Email already registered") - return crud.create_user(db=db, user=user) - - -@app.get("/users/", response_model=list[schemas.User]) -def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): - users = crud.get_users(db, skip=skip, limit=limit) - return users - - -@app.get("/users/{user_id}", response_model=schemas.User) -def read_user(user_id: int, db: Session = Depends(get_db)): - db_user = crud.get_user(db, user_id=user_id) - if db_user is None: - raise HTTPException(status_code=404, detail="User not found") - return db_user - - -@app.post("/users/{user_id}/items/", response_model=schemas.Item) -def create_item_for_user( - user_id: int, item: schemas.ItemCreate, db: Session = Depends(get_db) -): - return crud.create_user_item(db=db, item=item, user_id=user_id) - - -@app.get("/items/", response_model=list[schemas.Item]) -def read_items(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): - items = crud.get_items(db, skip=skip, limit=limit) - return items diff --git a/docs_src/sql_databases/sql_app_py39/models.py b/docs_src/sql_databases/sql_app_py39/models.py deleted file mode 100644 index 09ae2a8077..0000000000 --- a/docs_src/sql_databases/sql_app_py39/models.py +++ /dev/null @@ -1,26 +0,0 @@ -from sqlalchemy import Boolean, Column, ForeignKey, Integer, String -from sqlalchemy.orm import relationship - -from .database import Base - - -class User(Base): - __tablename__ = "users" - - id = Column(Integer, primary_key=True) - email = Column(String, unique=True, index=True) - hashed_password = Column(String) - is_active = Column(Boolean, default=True) - - items = relationship("Item", back_populates="owner") - - -class Item(Base): - __tablename__ = "items" - - id = Column(Integer, primary_key=True) - title = Column(String, index=True) - description = Column(String, index=True) - owner_id = Column(Integer, ForeignKey("users.id")) - - owner = relationship("User", back_populates="items") diff --git a/docs_src/sql_databases/sql_app_py39/schemas.py b/docs_src/sql_databases/sql_app_py39/schemas.py deleted file mode 100644 index dadc403d93..0000000000 --- a/docs_src/sql_databases/sql_app_py39/schemas.py +++ /dev/null @@ -1,37 +0,0 @@ -from typing import Union - -from pydantic import BaseModel - - -class ItemBase(BaseModel): - title: str - description: Union[str, None] = None - - -class ItemCreate(ItemBase): - pass - - -class Item(ItemBase): - id: int - owner_id: int - - class Config: - orm_mode = True - - -class UserBase(BaseModel): - email: str - - -class UserCreate(UserBase): - password: str - - -class User(UserBase): - id: int - is_active: bool - items: list[Item] = [] - - class Config: - orm_mode = True diff --git a/docs_src/sql_databases/sql_app_py39/tests/__init__.py b/docs_src/sql_databases/sql_app_py39/tests/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/docs_src/sql_databases/sql_app_py39/tests/test_sql_app.py b/docs_src/sql_databases/sql_app_py39/tests/test_sql_app.py deleted file mode 100644 index c60c3356f8..0000000000 --- a/docs_src/sql_databases/sql_app_py39/tests/test_sql_app.py +++ /dev/null @@ -1,47 +0,0 @@ -from fastapi.testclient import TestClient -from sqlalchemy import create_engine -from sqlalchemy.orm import sessionmaker - -from ..database import Base -from ..main import app, get_db - -SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db" - -engine = create_engine( - SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False} -) -TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) - - -Base.metadata.create_all(bind=engine) - - -def override_get_db(): - try: - db = TestingSessionLocal() - yield db - finally: - db.close() - - -app.dependency_overrides[get_db] = override_get_db - -client = TestClient(app) - - -def test_create_user(): - response = client.post( - "/users/", - json={"email": "deadpool@example.com", "password": "chimichangas4life"}, - ) - assert response.status_code == 200, response.text - data = response.json() - assert data["email"] == "deadpool@example.com" - assert "id" in data - user_id = data["id"] - - response = client.get(f"/users/{user_id}") - assert response.status_code == 200, response.text - data = response.json() - assert data["email"] == "deadpool@example.com" - assert data["id"] == user_id diff --git a/docs_src/sql_databases/tutorial001.py b/docs_src/sql_databases/tutorial001.py new file mode 100644 index 0000000000..be86ec0eeb --- /dev/null +++ b/docs_src/sql_databases/tutorial001.py @@ -0,0 +1,71 @@ +from typing import List, Union + +from fastapi import Depends, FastAPI, HTTPException, Query +from sqlmodel import Field, Session, SQLModel, create_engine, select + + +class Hero(SQLModel, table=True): + id: Union[int, None] = Field(default=None, primary_key=True) + name: str = Field(index=True) + age: Union[int, None] = Field(default=None, index=True) + secret_name: str + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +connect_args = {"check_same_thread": False} +engine = create_engine(sqlite_url, 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/") +def create_hero(hero: Hero, session: Session = Depends(get_session)) -> Hero: + session.add(hero) + session.commit() + session.refresh(hero) + return hero + + +@app.get("/heroes/") +def read_heroes( + session: Session = Depends(get_session), + offset: int = 0, + limit: int = Query(default=100, le=100), +) -> List[Hero]: + heroes = session.exec(select(Hero).offset(offset).limit(limit)).all() + return heroes + + +@app.get("/heroes/{hero_id}") +def read_hero(hero_id: int, session: Session = Depends(get_session)) -> Hero: + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + return hero + + +@app.delete("/heroes/{hero_id}") +def delete_hero(hero_id: int, session: Session = Depends(get_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/sql_databases/tutorial001_an.py b/docs_src/sql_databases/tutorial001_an.py new file mode 100644 index 0000000000..8c000d31c7 --- /dev/null +++ b/docs_src/sql_databases/tutorial001_an.py @@ -0,0 +1,74 @@ +from typing import List, Union + +from fastapi import Depends, FastAPI, HTTPException, Query +from sqlmodel import Field, Session, SQLModel, create_engine, select +from typing_extensions import Annotated + + +class Hero(SQLModel, table=True): + id: Union[int, None] = Field(default=None, primary_key=True) + name: str = Field(index=True) + age: Union[int, None] = Field(default=None, index=True) + secret_name: str + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +connect_args = {"check_same_thread": False} +engine = create_engine(sqlite_url, connect_args=connect_args) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +def get_session(): + with Session(engine) as session: + yield session + + +SessionDep = Annotated[Session, Depends(get_session)] + +app = FastAPI() + + +@app.on_event("startup") +def on_startup(): + create_db_and_tables() + + +@app.post("/heroes/") +def create_hero(hero: Hero, session: SessionDep) -> Hero: + session.add(hero) + session.commit() + session.refresh(hero) + return hero + + +@app.get("/heroes/") +def read_heroes( + session: SessionDep, + offset: int = 0, + limit: Annotated[int, Query(le=100)] = 100, +) -> List[Hero]: + heroes = session.exec(select(Hero).offset(offset).limit(limit)).all() + return heroes + + +@app.get("/heroes/{hero_id}") +def read_hero(hero_id: int, session: SessionDep) -> Hero: + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + return hero + + +@app.delete("/heroes/{hero_id}") +def delete_hero(hero_id: int, session: SessionDep): + 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/sql_databases/tutorial001_an_py310.py b/docs_src/sql_databases/tutorial001_an_py310.py new file mode 100644 index 0000000000..de1fb81faf --- /dev/null +++ b/docs_src/sql_databases/tutorial001_an_py310.py @@ -0,0 +1,73 @@ +from typing import Annotated + +from fastapi import Depends, FastAPI, HTTPException, Query +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) + age: int | None = Field(default=None, index=True) + secret_name: str + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +connect_args = {"check_same_thread": False} +engine = create_engine(sqlite_url, connect_args=connect_args) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +def get_session(): + with Session(engine) as session: + yield session + + +SessionDep = Annotated[Session, Depends(get_session)] + +app = FastAPI() + + +@app.on_event("startup") +def on_startup(): + create_db_and_tables() + + +@app.post("/heroes/") +def create_hero(hero: Hero, session: SessionDep) -> Hero: + session.add(hero) + session.commit() + session.refresh(hero) + return hero + + +@app.get("/heroes/") +def read_heroes( + session: SessionDep, + offset: int = 0, + limit: Annotated[int, Query(le=100)] = 100, +) -> list[Hero]: + heroes = session.exec(select(Hero).offset(offset).limit(limit)).all() + return heroes + + +@app.get("/heroes/{hero_id}") +def read_hero(hero_id: int, session: SessionDep) -> Hero: + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + return hero + + +@app.delete("/heroes/{hero_id}") +def delete_hero(hero_id: int, session: SessionDep): + 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/sql_databases/tutorial001_an_py39.py b/docs_src/sql_databases/tutorial001_an_py39.py new file mode 100644 index 0000000000..5958927461 --- /dev/null +++ b/docs_src/sql_databases/tutorial001_an_py39.py @@ -0,0 +1,73 @@ +from typing import Annotated, Union + +from fastapi import Depends, FastAPI, HTTPException, Query +from sqlmodel import Field, Session, SQLModel, create_engine, select + + +class Hero(SQLModel, table=True): + id: Union[int, None] = Field(default=None, primary_key=True) + name: str = Field(index=True) + age: Union[int, None] = Field(default=None, index=True) + secret_name: str + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +connect_args = {"check_same_thread": False} +engine = create_engine(sqlite_url, connect_args=connect_args) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +def get_session(): + with Session(engine) as session: + yield session + + +SessionDep = Annotated[Session, Depends(get_session)] + +app = FastAPI() + + +@app.on_event("startup") +def on_startup(): + create_db_and_tables() + + +@app.post("/heroes/") +def create_hero(hero: Hero, session: SessionDep) -> Hero: + session.add(hero) + session.commit() + session.refresh(hero) + return hero + + +@app.get("/heroes/") +def read_heroes( + session: SessionDep, + offset: int = 0, + limit: Annotated[int, Query(le=100)] = 100, +) -> list[Hero]: + heroes = session.exec(select(Hero).offset(offset).limit(limit)).all() + return heroes + + +@app.get("/heroes/{hero_id}") +def read_hero(hero_id: int, session: SessionDep) -> Hero: + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + return hero + + +@app.delete("/heroes/{hero_id}") +def delete_hero(hero_id: int, session: SessionDep): + 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/sql_databases/tutorial001_py310.py b/docs_src/sql_databases/tutorial001_py310.py new file mode 100644 index 0000000000..b58462e6a5 --- /dev/null +++ b/docs_src/sql_databases/tutorial001_py310.py @@ -0,0 +1,69 @@ +from fastapi import Depends, FastAPI, HTTPException, Query +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) + age: int | None = Field(default=None, index=True) + secret_name: str + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +connect_args = {"check_same_thread": False} +engine = create_engine(sqlite_url, 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/") +def create_hero(hero: Hero, session: Session = Depends(get_session)) -> Hero: + session.add(hero) + session.commit() + session.refresh(hero) + return hero + + +@app.get("/heroes/") +def read_heroes( + session: Session = Depends(get_session), + offset: int = 0, + limit: int = Query(default=100, le=100), +) -> list[Hero]: + heroes = session.exec(select(Hero).offset(offset).limit(limit)).all() + return heroes + + +@app.get("/heroes/{hero_id}") +def read_hero(hero_id: int, session: Session = Depends(get_session)) -> Hero: + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + return hero + + +@app.delete("/heroes/{hero_id}") +def delete_hero(hero_id: int, session: Session = Depends(get_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/sql_databases/tutorial001_py39.py b/docs_src/sql_databases/tutorial001_py39.py new file mode 100644 index 0000000000..410a52d0c0 --- /dev/null +++ b/docs_src/sql_databases/tutorial001_py39.py @@ -0,0 +1,71 @@ +from typing import Union + +from fastapi import Depends, FastAPI, HTTPException, Query +from sqlmodel import Field, Session, SQLModel, create_engine, select + + +class Hero(SQLModel, table=True): + id: Union[int, None] = Field(default=None, primary_key=True) + name: str = Field(index=True) + age: Union[int, None] = Field(default=None, index=True) + secret_name: str + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +connect_args = {"check_same_thread": False} +engine = create_engine(sqlite_url, 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/") +def create_hero(hero: Hero, session: Session = Depends(get_session)) -> Hero: + session.add(hero) + session.commit() + session.refresh(hero) + return hero + + +@app.get("/heroes/") +def read_heroes( + session: Session = Depends(get_session), + offset: int = 0, + limit: int = Query(default=100, le=100), +) -> list[Hero]: + heroes = session.exec(select(Hero).offset(offset).limit(limit)).all() + return heroes + + +@app.get("/heroes/{hero_id}") +def read_hero(hero_id: int, session: Session = Depends(get_session)) -> Hero: + hero = session.get(Hero, hero_id) + if not hero: + raise HTTPException(status_code=404, detail="Hero not found") + return hero + + +@app.delete("/heroes/{hero_id}") +def delete_hero(hero_id: int, session: Session = Depends(get_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/sql_databases/tutorial002.py b/docs_src/sql_databases/tutorial002.py new file mode 100644 index 0000000000..4350d19c61 --- /dev/null +++ b/docs_src/sql_databases/tutorial002.py @@ -0,0 +1,104 @@ +from typing import List, Union + +from fastapi import Depends, FastAPI, HTTPException, Query +from sqlmodel import Field, Session, SQLModel, create_engine, select + + +class HeroBase(SQLModel): + name: str = Field(index=True) + age: Union[int, None] = Field(default=None, index=True) + + +class Hero(HeroBase, table=True): + id: Union[int, None] = Field(default=None, primary_key=True) + secret_name: str + + +class HeroPublic(HeroBase): + id: int + + +class HeroCreate(HeroBase): + secret_name: str + + +class HeroUpdate(HeroBase): + name: Union[str, None] = None + age: Union[int, None] = None + secret_name: Union[str, 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, 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=HeroPublic) +def create_hero(hero: HeroCreate, session: Session = Depends(get_session)): + db_hero = Hero.model_validate(hero) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.get("/heroes/", response_model=List[HeroPublic]) +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=HeroPublic) +def read_hero(hero_id: int, session: Session = Depends(get_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=HeroPublic) +def update_hero( + hero_id: int, hero: HeroUpdate, session: Session = Depends(get_session) +): + hero_db = session.get(Hero, hero_id) + if not hero_db: + raise HTTPException(status_code=404, detail="Hero not found") + hero_data = hero.model_dump(exclude_unset=True) + hero_db.sqlmodel_update(hero_data) + session.add(hero_db) + session.commit() + session.refresh(hero_db) + return hero_db + + +@app.delete("/heroes/{hero_id}") +def delete_hero(hero_id: int, session: Session = Depends(get_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/sql_databases/tutorial002_an.py b/docs_src/sql_databases/tutorial002_an.py new file mode 100644 index 0000000000..15e3d7c3a5 --- /dev/null +++ b/docs_src/sql_databases/tutorial002_an.py @@ -0,0 +1,104 @@ +from typing import List, Union + +from fastapi import Depends, FastAPI, HTTPException, Query +from sqlmodel import Field, Session, SQLModel, create_engine, select +from typing_extensions import Annotated + + +class HeroBase(SQLModel): + name: str = Field(index=True) + age: Union[int, None] = Field(default=None, index=True) + + +class Hero(HeroBase, table=True): + id: Union[int, None] = Field(default=None, primary_key=True) + secret_name: str + + +class HeroPublic(HeroBase): + id: int + + +class HeroCreate(HeroBase): + secret_name: str + + +class HeroUpdate(HeroBase): + name: Union[str, None] = None + age: Union[int, None] = None + secret_name: Union[str, 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, connect_args=connect_args) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +def get_session(): + with Session(engine) as session: + yield session + + +SessionDep = Annotated[Session, Depends(get_session)] +app = FastAPI() + + +@app.on_event("startup") +def on_startup(): + create_db_and_tables() + + +@app.post("/heroes/", response_model=HeroPublic) +def create_hero(hero: HeroCreate, session: SessionDep): + db_hero = Hero.model_validate(hero) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.get("/heroes/", response_model=List[HeroPublic]) +def read_heroes( + session: SessionDep, + offset: int = 0, + limit: Annotated[int, Query(le=100)] = 100, +): + heroes = session.exec(select(Hero).offset(offset).limit(limit)).all() + return heroes + + +@app.get("/heroes/{hero_id}", response_model=HeroPublic) +def read_hero(hero_id: int, session: SessionDep): + 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=HeroPublic) +def update_hero(hero_id: int, hero: HeroUpdate, session: SessionDep): + hero_db = session.get(Hero, hero_id) + if not hero_db: + raise HTTPException(status_code=404, detail="Hero not found") + hero_data = hero.model_dump(exclude_unset=True) + hero_db.sqlmodel_update(hero_data) + session.add(hero_db) + session.commit() + session.refresh(hero_db) + return hero_db + + +@app.delete("/heroes/{hero_id}") +def delete_hero(hero_id: int, session: SessionDep): + 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/sql_databases/tutorial002_an_py310.py b/docs_src/sql_databases/tutorial002_an_py310.py new file mode 100644 index 0000000000..64c554b8a2 --- /dev/null +++ b/docs_src/sql_databases/tutorial002_an_py310.py @@ -0,0 +1,103 @@ +from typing import Annotated + +from fastapi import Depends, FastAPI, HTTPException, Query +from sqlmodel import Field, Session, SQLModel, create_engine, select + + +class HeroBase(SQLModel): + name: str = Field(index=True) + age: int | None = Field(default=None, index=True) + + +class Hero(HeroBase, table=True): + id: int | None = Field(default=None, primary_key=True) + secret_name: str + + +class HeroPublic(HeroBase): + id: int + + +class HeroCreate(HeroBase): + secret_name: str + + +class HeroUpdate(HeroBase): + name: str | None = None + age: int | None = None + secret_name: str | 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, connect_args=connect_args) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +def get_session(): + with Session(engine) as session: + yield session + + +SessionDep = Annotated[Session, Depends(get_session)] +app = FastAPI() + + +@app.on_event("startup") +def on_startup(): + create_db_and_tables() + + +@app.post("/heroes/", response_model=HeroPublic) +def create_hero(hero: HeroCreate, session: SessionDep): + db_hero = Hero.model_validate(hero) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.get("/heroes/", response_model=list[HeroPublic]) +def read_heroes( + session: SessionDep, + offset: int = 0, + limit: Annotated[int, Query(le=100)] = 100, +): + heroes = session.exec(select(Hero).offset(offset).limit(limit)).all() + return heroes + + +@app.get("/heroes/{hero_id}", response_model=HeroPublic) +def read_hero(hero_id: int, session: SessionDep): + 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=HeroPublic) +def update_hero(hero_id: int, hero: HeroUpdate, session: SessionDep): + hero_db = session.get(Hero, hero_id) + if not hero_db: + raise HTTPException(status_code=404, detail="Hero not found") + hero_data = hero.model_dump(exclude_unset=True) + hero_db.sqlmodel_update(hero_data) + session.add(hero_db) + session.commit() + session.refresh(hero_db) + return hero_db + + +@app.delete("/heroes/{hero_id}") +def delete_hero(hero_id: int, session: SessionDep): + 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/sql_databases/tutorial002_an_py39.py b/docs_src/sql_databases/tutorial002_an_py39.py new file mode 100644 index 0000000000..a8a0721ff6 --- /dev/null +++ b/docs_src/sql_databases/tutorial002_an_py39.py @@ -0,0 +1,103 @@ +from typing import Annotated, Union + +from fastapi import Depends, FastAPI, HTTPException, Query +from sqlmodel import Field, Session, SQLModel, create_engine, select + + +class HeroBase(SQLModel): + name: str = Field(index=True) + age: Union[int, None] = Field(default=None, index=True) + + +class Hero(HeroBase, table=True): + id: Union[int, None] = Field(default=None, primary_key=True) + secret_name: str + + +class HeroPublic(HeroBase): + id: int + + +class HeroCreate(HeroBase): + secret_name: str + + +class HeroUpdate(HeroBase): + name: Union[str, None] = None + age: Union[int, None] = None + secret_name: Union[str, 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, connect_args=connect_args) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +def get_session(): + with Session(engine) as session: + yield session + + +SessionDep = Annotated[Session, Depends(get_session)] +app = FastAPI() + + +@app.on_event("startup") +def on_startup(): + create_db_and_tables() + + +@app.post("/heroes/", response_model=HeroPublic) +def create_hero(hero: HeroCreate, session: SessionDep): + db_hero = Hero.model_validate(hero) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.get("/heroes/", response_model=list[HeroPublic]) +def read_heroes( + session: SessionDep, + offset: int = 0, + limit: Annotated[int, Query(le=100)] = 100, +): + heroes = session.exec(select(Hero).offset(offset).limit(limit)).all() + return heroes + + +@app.get("/heroes/{hero_id}", response_model=HeroPublic) +def read_hero(hero_id: int, session: SessionDep): + 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=HeroPublic) +def update_hero(hero_id: int, hero: HeroUpdate, session: SessionDep): + hero_db = session.get(Hero, hero_id) + if not hero_db: + raise HTTPException(status_code=404, detail="Hero not found") + hero_data = hero.model_dump(exclude_unset=True) + hero_db.sqlmodel_update(hero_data) + session.add(hero_db) + session.commit() + session.refresh(hero_db) + return hero_db + + +@app.delete("/heroes/{hero_id}") +def delete_hero(hero_id: int, session: SessionDep): + 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/sql_databases/tutorial002_py310.py b/docs_src/sql_databases/tutorial002_py310.py new file mode 100644 index 0000000000..ec3d68db53 --- /dev/null +++ b/docs_src/sql_databases/tutorial002_py310.py @@ -0,0 +1,102 @@ +from fastapi import Depends, FastAPI, HTTPException, Query +from sqlmodel import Field, Session, SQLModel, create_engine, select + + +class HeroBase(SQLModel): + name: str = Field(index=True) + age: int | None = Field(default=None, index=True) + + +class Hero(HeroBase, table=True): + id: int | None = Field(default=None, primary_key=True) + secret_name: str + + +class HeroPublic(HeroBase): + id: int + + +class HeroCreate(HeroBase): + secret_name: str + + +class HeroUpdate(HeroBase): + name: str | None = None + age: int | None = None + secret_name: str | 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, 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=HeroPublic) +def create_hero(hero: HeroCreate, session: Session = Depends(get_session)): + db_hero = Hero.model_validate(hero) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.get("/heroes/", response_model=list[HeroPublic]) +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=HeroPublic) +def read_hero(hero_id: int, session: Session = Depends(get_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=HeroPublic) +def update_hero( + hero_id: int, hero: HeroUpdate, session: Session = Depends(get_session) +): + hero_db = session.get(Hero, hero_id) + if not hero_db: + raise HTTPException(status_code=404, detail="Hero not found") + hero_data = hero.model_dump(exclude_unset=True) + hero_db.sqlmodel_update(hero_data) + session.add(hero_db) + session.commit() + session.refresh(hero_db) + return hero_db + + +@app.delete("/heroes/{hero_id}") +def delete_hero(hero_id: int, session: Session = Depends(get_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/sql_databases/tutorial002_py39.py b/docs_src/sql_databases/tutorial002_py39.py new file mode 100644 index 0000000000..d8f5dd0901 --- /dev/null +++ b/docs_src/sql_databases/tutorial002_py39.py @@ -0,0 +1,104 @@ +from typing import Union + +from fastapi import Depends, FastAPI, HTTPException, Query +from sqlmodel import Field, Session, SQLModel, create_engine, select + + +class HeroBase(SQLModel): + name: str = Field(index=True) + age: Union[int, None] = Field(default=None, index=True) + + +class Hero(HeroBase, table=True): + id: Union[int, None] = Field(default=None, primary_key=True) + secret_name: str + + +class HeroPublic(HeroBase): + id: int + + +class HeroCreate(HeroBase): + secret_name: str + + +class HeroUpdate(HeroBase): + name: Union[str, None] = None + age: Union[int, None] = None + secret_name: Union[str, 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, 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=HeroPublic) +def create_hero(hero: HeroCreate, session: Session = Depends(get_session)): + db_hero = Hero.model_validate(hero) + session.add(db_hero) + session.commit() + session.refresh(db_hero) + return db_hero + + +@app.get("/heroes/", response_model=list[HeroPublic]) +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=HeroPublic) +def read_hero(hero_id: int, session: Session = Depends(get_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=HeroPublic) +def update_hero( + hero_id: int, hero: HeroUpdate, session: Session = Depends(get_session) +): + hero_db = session.get(Hero, hero_id) + if not hero_db: + raise HTTPException(status_code=404, detail="Hero not found") + hero_data = hero.model_dump(exclude_unset=True) + hero_db.sqlmodel_update(hero_data) + session.add(hero_db) + session.commit() + session.refresh(hero_db) + return hero_db + + +@app.delete("/heroes/{hero_id}") +def delete_hero(hero_id: int, session: Session = Depends(get_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/requirements-docs.txt b/requirements-docs.txt index c05bd51e32..1639159aff 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -16,4 +16,4 @@ griffe-typingdoc==0.2.7 # For griffe, it formats with black black==24.3.0 mkdocs-macros-plugin==1.0.5 -markdown-include-variants==0.0.1 +markdown-include-variants==0.0.3 diff --git a/requirements-tests.txt b/requirements-tests.txt index 7b1f7ea1a6..189fcaf7e9 100644 --- a/requirements-tests.txt +++ b/requirements-tests.txt @@ -4,10 +4,7 @@ pytest >=7.1.3,<8.0.0 coverage[toml] >= 6.5.0,< 8.0 mypy ==1.8.0 dirty-equals ==0.6.0 -# TODO: once removing databases from tutorial, upgrade SQLAlchemy -# probably when including SQLModel -sqlalchemy >=1.3.18,<2.0.33 -databases[sqlite] >=0.3.2,<0.7.0 +sqlmodel==0.0.22 flask >=1.1.2,<3.0.0 anyio[trio] >=3.2.1,<4.0.0 PyJWT==2.8.0 diff --git a/scripts/playwright/sql_databases/image01.py b/scripts/playwright/sql_databases/image01.py new file mode 100644 index 0000000000..0dd6f25145 --- /dev/null +++ b/scripts/playwright/sql_databases/image01.py @@ -0,0 +1,37 @@ +import subprocess +import time + +import httpx +from playwright.sync_api import Playwright, sync_playwright + + +# Run playwright codegen to generate the code below, copy paste the sections in run() +def run(playwright: Playwright) -> None: + browser = playwright.chromium.launch(headless=False) + # Update the viewport manually + context = browser.new_context(viewport={"width": 960, "height": 1080}) + page = context.new_page() + page.goto("http://localhost:8000/docs") + page.get_by_label("post /heroes/").click() + # Manually add the screenshot + page.screenshot(path="docs/en/docs/img/tutorial/sql-databases/image01.png") + + # --------------------- + context.close() + browser.close() + + +process = subprocess.Popen( + ["fastapi", "run", "docs_src/sql_databases/tutorial001.py"], +) +try: + for _ in range(3): + try: + response = httpx.get("http://localhost:8000/docs") + except httpx.ConnectError: + time.sleep(1) + break + with sync_playwright() as playwright: + run(playwright) +finally: + process.terminate() diff --git a/scripts/playwright/sql_databases/image02.py b/scripts/playwright/sql_databases/image02.py new file mode 100644 index 0000000000..6c4f685e86 --- /dev/null +++ b/scripts/playwright/sql_databases/image02.py @@ -0,0 +1,37 @@ +import subprocess +import time + +import httpx +from playwright.sync_api import Playwright, sync_playwright + + +# Run playwright codegen to generate the code below, copy paste the sections in run() +def run(playwright: Playwright) -> None: + browser = playwright.chromium.launch(headless=False) + # Update the viewport manually + context = browser.new_context(viewport={"width": 960, "height": 1080}) + page = context.new_page() + page.goto("http://localhost:8000/docs") + page.get_by_label("post /heroes/").click() + # Manually add the screenshot + page.screenshot(path="docs/en/docs/img/tutorial/sql-databases/image02.png") + + # --------------------- + context.close() + browser.close() + + +process = subprocess.Popen( + ["fastapi", "run", "docs_src/sql_databases/tutorial002.py"], +) +try: + for _ in range(3): + try: + response = httpx.get("http://localhost:8000/docs") + except httpx.ConnectError: + time.sleep(1) + break + with sync_playwright() as playwright: + run(playwright) +finally: + process.terminate() diff --git a/tests/test_tutorial/test_async_sql_databases/__init__.py b/tests/test_tutorial/test_async_sql_databases/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/test_tutorial/test_async_sql_databases/test_tutorial001.py b/tests/test_tutorial/test_async_sql_databases/test_tutorial001.py deleted file mode 100644 index 13568a5328..0000000000 --- a/tests/test_tutorial/test_async_sql_databases/test_tutorial001.py +++ /dev/null @@ -1,146 +0,0 @@ -import pytest -from fastapi import FastAPI -from fastapi.testclient import TestClient - -from ...utils import needs_pydanticv1 - - -@pytest.fixture(name="app", scope="module") -def get_app(): - with pytest.warns(DeprecationWarning): - from docs_src.async_sql_databases.tutorial001 import app - yield app - - -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_create_read(app: FastAPI): - with TestClient(app) as client: - note = {"text": "Foo bar", "completed": False} - response = client.post("/notes/", json=note) - assert response.status_code == 200, response.text - data = response.json() - assert data["text"] == note["text"] - assert data["completed"] == note["completed"] - assert "id" in data - response = client.get("/notes/") - assert response.status_code == 200, response.text - assert data in response.json() - - -def test_openapi_schema(app: FastAPI): - with TestClient(app) as client: - 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": { - "/notes/": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": { - "title": "Response Read Notes Notes Get", - "type": "array", - "items": { - "$ref": "#/components/schemas/Note" - }, - } - } - }, - } - }, - "summary": "Read Notes", - "operationId": "read_notes_notes__get", - }, - "post": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/Note"} - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Create Note", - "operationId": "create_note_notes__post", - "requestBody": { - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/NoteIn"} - } - }, - "required": True, - }, - }, - } - }, - "components": { - "schemas": { - "NoteIn": { - "title": "NoteIn", - "required": ["text", "completed"], - "type": "object", - "properties": { - "text": {"title": "Text", "type": "string"}, - "completed": {"title": "Completed", "type": "boolean"}, - }, - }, - "Note": { - "title": "Note", - "required": ["id", "text", "completed"], - "type": "object", - "properties": { - "id": {"title": "Id", "type": "integer"}, - "text": {"title": "Text", "type": "string"}, - "completed": {"title": "Completed", "type": "boolean"}, - }, - }, - "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"}, - }, - }, - "HTTPValidationError": { - "title": "HTTPValidationError", - "type": "object", - "properties": { - "detail": { - "title": "Detail", - "type": "array", - "items": { - "$ref": "#/components/schemas/ValidationError" - }, - } - }, - }, - } - }, - } diff --git a/tests/test_tutorial/test_sql_databases/test_sql_databases.py b/tests/test_tutorial/test_sql_databases/test_sql_databases.py deleted file mode 100644 index e3e2b36a80..0000000000 --- a/tests/test_tutorial/test_sql_databases/test_sql_databases.py +++ /dev/null @@ -1,419 +0,0 @@ -import importlib -import os -from pathlib import Path - -import pytest -from dirty_equals import IsDict -from fastapi.testclient import TestClient - -from ...utils import needs_pydanticv1 - - -@pytest.fixture(scope="module") -def client(tmp_path_factory: pytest.TempPathFactory): - tmp_path = tmp_path_factory.mktemp("data") - cwd = os.getcwd() - os.chdir(tmp_path) - test_db = Path("./sql_app.db") - if test_db.is_file(): # pragma: nocover - test_db.unlink() - # Import while creating the client to create the DB after starting the test session - from docs_src.sql_databases.sql_app import main - - # Ensure import side effects are re-executed - importlib.reload(main) - with TestClient(main.app) as c: - yield c - if test_db.is_file(): # pragma: nocover - test_db.unlink() - os.chdir(cwd) - - -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_create_user(client): - test_user = {"email": "johndoe@example.com", "password": "secret"} - response = client.post("/users/", json=test_user) - assert response.status_code == 200, response.text - data = response.json() - assert test_user["email"] == data["email"] - assert "id" in data - response = client.post("/users/", json=test_user) - assert response.status_code == 400, response.text - - -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_get_user(client): - response = client.get("/users/1") - assert response.status_code == 200, response.text - data = response.json() - assert "email" in data - assert "id" in data - - -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_nonexistent_user(client): - response = client.get("/users/999") - assert response.status_code == 404, response.text - - -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_get_users(client): - response = client.get("/users/") - assert response.status_code == 200, response.text - data = response.json() - assert "email" in data[0] - assert "id" in data[0] - - -# TODO: pv2 add Pydantic v2 version -@needs_pydanticv1 -def test_create_item(client): - item = {"title": "Foo", "description": "Something that fights"} - response = client.post("/users/1/items/", json=item) - assert response.status_code == 200, response.text - item_data = response.json() - assert item["title"] == item_data["title"] - assert item["description"] == item_data["description"] - assert "id" in item_data - assert "owner_id" in item_data - response = client.get("/users/1") - assert response.status_code == 200, response.text - user_data = response.json() - item_to_check = [it for it in user_data["items"] if it["id"] == item_data["id"]][0] - assert item_to_check["title"] == item["title"] - assert item_to_check["description"] == item["description"] - - -# TODO: pv2 add Pydantic v2 version -@needs_pydanticv1 -def test_read_items(client): - response = client.get("/items/") - assert response.status_code == 200, response.text - data = response.json() - assert data - first_item = data[0] - assert "title" in first_item - assert "description" in first_item - - -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_openapi_schema(client: TestClient): - 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": { - "/users/": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": { - "title": "Response Read Users Users Get", - "type": "array", - "items": {"$ref": "#/components/schemas/User"}, - } - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read Users", - "operationId": "read_users_users__get", - "parameters": [ - { - "required": False, - "schema": { - "title": "Skip", - "type": "integer", - "default": 0, - }, - "name": "skip", - "in": "query", - }, - { - "required": False, - "schema": { - "title": "Limit", - "type": "integer", - "default": 100, - }, - "name": "limit", - "in": "query", - }, - ], - }, - "post": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/User"} - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Create User", - "operationId": "create_user_users__post", - "requestBody": { - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/UserCreate"} - } - }, - "required": True, - }, - }, - }, - "/users/{user_id}": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/User"} - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read User", - "operationId": "read_user_users__user_id__get", - "parameters": [ - { - "required": True, - "schema": {"title": "User Id", "type": "integer"}, - "name": "user_id", - "in": "path", - } - ], - } - }, - "/users/{user_id}/items/": { - "post": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/Item"} - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Create Item For User", - "operationId": "create_item_for_user_users__user_id__items__post", - "parameters": [ - { - "required": True, - "schema": {"title": "User Id", "type": "integer"}, - "name": "user_id", - "in": "path", - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/ItemCreate"} - } - }, - "required": True, - }, - } - }, - "/items/": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": { - "title": "Response Read Items Items Get", - "type": "array", - "items": {"$ref": "#/components/schemas/Item"}, - } - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read Items", - "operationId": "read_items_items__get", - "parameters": [ - { - "required": False, - "schema": { - "title": "Skip", - "type": "integer", - "default": 0, - }, - "name": "skip", - "in": "query", - }, - { - "required": False, - "schema": { - "title": "Limit", - "type": "integer", - "default": 100, - }, - "name": "limit", - "in": "query", - }, - ], - } - }, - }, - "components": { - "schemas": { - "ItemCreate": { - "title": "ItemCreate", - "required": ["title"], - "type": "object", - "properties": { - "title": {"title": "Title", "type": "string"}, - "description": IsDict( - { - "title": "Description", - "anyOf": [{"type": "string"}, {"type": "null"}], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - {"title": "Description", "type": "string"} - ), - }, - }, - "Item": { - "title": "Item", - "required": ["title", "id", "owner_id"], - "type": "object", - "properties": { - "title": {"title": "Title", "type": "string"}, - "description": IsDict( - { - "title": "Description", - "anyOf": [{"type": "string"}, {"type": "null"}], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - {"title": "Description", "type": "string"}, - ), - "id": {"title": "Id", "type": "integer"}, - "owner_id": {"title": "Owner Id", "type": "integer"}, - }, - }, - "User": { - "title": "User", - "required": ["email", "id", "is_active"], - "type": "object", - "properties": { - "email": {"title": "Email", "type": "string"}, - "id": {"title": "Id", "type": "integer"}, - "is_active": {"title": "Is Active", "type": "boolean"}, - "items": { - "title": "Items", - "type": "array", - "items": {"$ref": "#/components/schemas/Item"}, - "default": [], - }, - }, - }, - "UserCreate": { - "title": "UserCreate", - "required": ["email", "password"], - "type": "object", - "properties": { - "email": {"title": "Email", "type": "string"}, - "password": {"title": "Password", "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"}, - }, - }, - "HTTPValidationError": { - "title": "HTTPValidationError", - "type": "object", - "properties": { - "detail": { - "title": "Detail", - "type": "array", - "items": {"$ref": "#/components/schemas/ValidationError"}, - } - }, - }, - } - }, - } diff --git a/tests/test_tutorial/test_sql_databases/test_sql_databases_middleware.py b/tests/test_tutorial/test_sql_databases/test_sql_databases_middleware.py deleted file mode 100644 index 73b97e09d9..0000000000 --- a/tests/test_tutorial/test_sql_databases/test_sql_databases_middleware.py +++ /dev/null @@ -1,421 +0,0 @@ -import importlib -from pathlib import Path - -import pytest -from dirty_equals import IsDict -from fastapi.testclient import TestClient - -from ...utils import needs_pydanticv1 - - -@pytest.fixture(scope="module") -def client(): - test_db = Path("./sql_app.db") - if test_db.is_file(): # pragma: nocover - test_db.unlink() - # Import while creating the client to create the DB after starting the test session - from docs_src.sql_databases.sql_app import alt_main - - # Ensure import side effects are re-executed - importlib.reload(alt_main) - - with TestClient(alt_main.app) as c: - yield c - if test_db.is_file(): # pragma: nocover - test_db.unlink() - - -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_create_user(client): - test_user = {"email": "johndoe@example.com", "password": "secret"} - response = client.post("/users/", json=test_user) - assert response.status_code == 200, response.text - data = response.json() - assert test_user["email"] == data["email"] - assert "id" in data - response = client.post("/users/", json=test_user) - assert response.status_code == 400, response.text - - -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_get_user(client): - response = client.get("/users/1") - assert response.status_code == 200, response.text - data = response.json() - assert "email" in data - assert "id" in data - - -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_nonexistent_user(client): - response = client.get("/users/999") - assert response.status_code == 404, response.text - - -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_get_users(client): - response = client.get("/users/") - assert response.status_code == 200, response.text - data = response.json() - assert "email" in data[0] - assert "id" in data[0] - - -# TODO: pv2 add Pydantic v2 version -@needs_pydanticv1 -def test_create_item(client): - item = {"title": "Foo", "description": "Something that fights"} - response = client.post("/users/1/items/", json=item) - assert response.status_code == 200, response.text - item_data = response.json() - assert item["title"] == item_data["title"] - assert item["description"] == item_data["description"] - assert "id" in item_data - assert "owner_id" in item_data - response = client.get("/users/1") - assert response.status_code == 200, response.text - user_data = response.json() - item_to_check = [it for it in user_data["items"] if it["id"] == item_data["id"]][0] - assert item_to_check["title"] == item["title"] - assert item_to_check["description"] == item["description"] - response = client.get("/users/1") - assert response.status_code == 200, response.text - user_data = response.json() - item_to_check = [it for it in user_data["items"] if it["id"] == item_data["id"]][0] - assert item_to_check["title"] == item["title"] - assert item_to_check["description"] == item["description"] - - -# TODO: pv2 add Pydantic v2 version -@needs_pydanticv1 -def test_read_items(client): - response = client.get("/items/") - assert response.status_code == 200, response.text - data = response.json() - assert data - first_item = data[0] - assert "title" in first_item - assert "description" in first_item - - -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_openapi_schema(client: TestClient): - 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": { - "/users/": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": { - "title": "Response Read Users Users Get", - "type": "array", - "items": {"$ref": "#/components/schemas/User"}, - } - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read Users", - "operationId": "read_users_users__get", - "parameters": [ - { - "required": False, - "schema": { - "title": "Skip", - "type": "integer", - "default": 0, - }, - "name": "skip", - "in": "query", - }, - { - "required": False, - "schema": { - "title": "Limit", - "type": "integer", - "default": 100, - }, - "name": "limit", - "in": "query", - }, - ], - }, - "post": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/User"} - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Create User", - "operationId": "create_user_users__post", - "requestBody": { - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/UserCreate"} - } - }, - "required": True, - }, - }, - }, - "/users/{user_id}": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/User"} - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read User", - "operationId": "read_user_users__user_id__get", - "parameters": [ - { - "required": True, - "schema": {"title": "User Id", "type": "integer"}, - "name": "user_id", - "in": "path", - } - ], - } - }, - "/users/{user_id}/items/": { - "post": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/Item"} - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Create Item For User", - "operationId": "create_item_for_user_users__user_id__items__post", - "parameters": [ - { - "required": True, - "schema": {"title": "User Id", "type": "integer"}, - "name": "user_id", - "in": "path", - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/ItemCreate"} - } - }, - "required": True, - }, - } - }, - "/items/": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": { - "title": "Response Read Items Items Get", - "type": "array", - "items": {"$ref": "#/components/schemas/Item"}, - } - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read Items", - "operationId": "read_items_items__get", - "parameters": [ - { - "required": False, - "schema": { - "title": "Skip", - "type": "integer", - "default": 0, - }, - "name": "skip", - "in": "query", - }, - { - "required": False, - "schema": { - "title": "Limit", - "type": "integer", - "default": 100, - }, - "name": "limit", - "in": "query", - }, - ], - } - }, - }, - "components": { - "schemas": { - "ItemCreate": { - "title": "ItemCreate", - "required": ["title"], - "type": "object", - "properties": { - "title": {"title": "Title", "type": "string"}, - "description": IsDict( - { - "title": "Description", - "anyOf": [{"type": "string"}, {"type": "null"}], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - {"title": "Description", "type": "string"} - ), - }, - }, - "Item": { - "title": "Item", - "required": ["title", "id", "owner_id"], - "type": "object", - "properties": { - "title": {"title": "Title", "type": "string"}, - "description": IsDict( - { - "title": "Description", - "anyOf": [{"type": "string"}, {"type": "null"}], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - {"title": "Description", "type": "string"}, - ), - "id": {"title": "Id", "type": "integer"}, - "owner_id": {"title": "Owner Id", "type": "integer"}, - }, - }, - "User": { - "title": "User", - "required": ["email", "id", "is_active"], - "type": "object", - "properties": { - "email": {"title": "Email", "type": "string"}, - "id": {"title": "Id", "type": "integer"}, - "is_active": {"title": "Is Active", "type": "boolean"}, - "items": { - "title": "Items", - "type": "array", - "items": {"$ref": "#/components/schemas/Item"}, - "default": [], - }, - }, - }, - "UserCreate": { - "title": "UserCreate", - "required": ["email", "password"], - "type": "object", - "properties": { - "email": {"title": "Email", "type": "string"}, - "password": {"title": "Password", "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"}, - }, - }, - "HTTPValidationError": { - "title": "HTTPValidationError", - "type": "object", - "properties": { - "detail": { - "title": "Detail", - "type": "array", - "items": {"$ref": "#/components/schemas/ValidationError"}, - } - }, - }, - } - }, - } diff --git a/tests/test_tutorial/test_sql_databases/test_sql_databases_middleware_py310.py b/tests/test_tutorial/test_sql_databases/test_sql_databases_middleware_py310.py deleted file mode 100644 index a078f012a9..0000000000 --- a/tests/test_tutorial/test_sql_databases/test_sql_databases_middleware_py310.py +++ /dev/null @@ -1,433 +0,0 @@ -import importlib -import os -from pathlib import Path - -import pytest -from dirty_equals import IsDict -from fastapi.testclient import TestClient - -from ...utils import needs_py310, needs_pydanticv1 - - -@pytest.fixture(scope="module") -def client(tmp_path_factory: pytest.TempPathFactory): - tmp_path = tmp_path_factory.mktemp("data") - cwd = os.getcwd() - os.chdir(tmp_path) - test_db = Path("./sql_app.db") - if test_db.is_file(): # pragma: nocover - test_db.unlink() - # Import while creating the client to create the DB after starting the test session - from docs_src.sql_databases.sql_app_py310 import alt_main - - # Ensure import side effects are re-executed - importlib.reload(alt_main) - - with TestClient(alt_main.app) as c: - yield c - if test_db.is_file(): # pragma: nocover - test_db.unlink() - os.chdir(cwd) - - -@needs_py310 -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_create_user(client): - test_user = {"email": "johndoe@example.com", "password": "secret"} - response = client.post("/users/", json=test_user) - assert response.status_code == 200, response.text - data = response.json() - assert test_user["email"] == data["email"] - assert "id" in data - response = client.post("/users/", json=test_user) - assert response.status_code == 400, response.text - - -@needs_py310 -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_get_user(client): - response = client.get("/users/1") - assert response.status_code == 200, response.text - data = response.json() - assert "email" in data - assert "id" in data - - -@needs_py310 -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_nonexistent_user(client): - response = client.get("/users/999") - assert response.status_code == 404, response.text - - -@needs_py310 -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_get_users(client): - response = client.get("/users/") - assert response.status_code == 200, response.text - data = response.json() - assert "email" in data[0] - assert "id" in data[0] - - -@needs_py310 -# TODO: pv2 add Pydantic v2 version -@needs_pydanticv1 -def test_create_item(client): - item = {"title": "Foo", "description": "Something that fights"} - response = client.post("/users/1/items/", json=item) - assert response.status_code == 200, response.text - item_data = response.json() - assert item["title"] == item_data["title"] - assert item["description"] == item_data["description"] - assert "id" in item_data - assert "owner_id" in item_data - response = client.get("/users/1") - assert response.status_code == 200, response.text - user_data = response.json() - item_to_check = [it for it in user_data["items"] if it["id"] == item_data["id"]][0] - assert item_to_check["title"] == item["title"] - assert item_to_check["description"] == item["description"] - response = client.get("/users/1") - assert response.status_code == 200, response.text - user_data = response.json() - item_to_check = [it for it in user_data["items"] if it["id"] == item_data["id"]][0] - assert item_to_check["title"] == item["title"] - assert item_to_check["description"] == item["description"] - - -@needs_py310 -# TODO: pv2 add Pydantic v2 version -@needs_pydanticv1 -def test_read_items(client): - response = client.get("/items/") - assert response.status_code == 200, response.text - data = response.json() - assert data - first_item = data[0] - assert "title" in first_item - assert "description" in first_item - - -@needs_py310 -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_openapi_schema(client: TestClient): - 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": { - "/users/": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": { - "title": "Response Read Users Users Get", - "type": "array", - "items": {"$ref": "#/components/schemas/User"}, - } - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read Users", - "operationId": "read_users_users__get", - "parameters": [ - { - "required": False, - "schema": { - "title": "Skip", - "type": "integer", - "default": 0, - }, - "name": "skip", - "in": "query", - }, - { - "required": False, - "schema": { - "title": "Limit", - "type": "integer", - "default": 100, - }, - "name": "limit", - "in": "query", - }, - ], - }, - "post": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/User"} - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Create User", - "operationId": "create_user_users__post", - "requestBody": { - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/UserCreate"} - } - }, - "required": True, - }, - }, - }, - "/users/{user_id}": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/User"} - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read User", - "operationId": "read_user_users__user_id__get", - "parameters": [ - { - "required": True, - "schema": {"title": "User Id", "type": "integer"}, - "name": "user_id", - "in": "path", - } - ], - } - }, - "/users/{user_id}/items/": { - "post": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/Item"} - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Create Item For User", - "operationId": "create_item_for_user_users__user_id__items__post", - "parameters": [ - { - "required": True, - "schema": {"title": "User Id", "type": "integer"}, - "name": "user_id", - "in": "path", - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/ItemCreate"} - } - }, - "required": True, - }, - } - }, - "/items/": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": { - "title": "Response Read Items Items Get", - "type": "array", - "items": {"$ref": "#/components/schemas/Item"}, - } - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read Items", - "operationId": "read_items_items__get", - "parameters": [ - { - "required": False, - "schema": { - "title": "Skip", - "type": "integer", - "default": 0, - }, - "name": "skip", - "in": "query", - }, - { - "required": False, - "schema": { - "title": "Limit", - "type": "integer", - "default": 100, - }, - "name": "limit", - "in": "query", - }, - ], - } - }, - }, - "components": { - "schemas": { - "ItemCreate": { - "title": "ItemCreate", - "required": ["title"], - "type": "object", - "properties": { - "title": {"title": "Title", "type": "string"}, - "description": IsDict( - { - "title": "Description", - "anyOf": [{"type": "string"}, {"type": "null"}], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - {"title": "Description", "type": "string"} - ), - }, - }, - "Item": { - "title": "Item", - "required": ["title", "id", "owner_id"], - "type": "object", - "properties": { - "title": {"title": "Title", "type": "string"}, - "description": IsDict( - { - "title": "Description", - "anyOf": [{"type": "string"}, {"type": "null"}], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - {"title": "Description", "type": "string"}, - ), - "id": {"title": "Id", "type": "integer"}, - "owner_id": {"title": "Owner Id", "type": "integer"}, - }, - }, - "User": { - "title": "User", - "required": ["email", "id", "is_active"], - "type": "object", - "properties": { - "email": {"title": "Email", "type": "string"}, - "id": {"title": "Id", "type": "integer"}, - "is_active": {"title": "Is Active", "type": "boolean"}, - "items": { - "title": "Items", - "type": "array", - "items": {"$ref": "#/components/schemas/Item"}, - "default": [], - }, - }, - }, - "UserCreate": { - "title": "UserCreate", - "required": ["email", "password"], - "type": "object", - "properties": { - "email": {"title": "Email", "type": "string"}, - "password": {"title": "Password", "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"}, - }, - }, - "HTTPValidationError": { - "title": "HTTPValidationError", - "type": "object", - "properties": { - "detail": { - "title": "Detail", - "type": "array", - "items": {"$ref": "#/components/schemas/ValidationError"}, - } - }, - }, - } - }, - } diff --git a/tests/test_tutorial/test_sql_databases/test_sql_databases_middleware_py39.py b/tests/test_tutorial/test_sql_databases/test_sql_databases_middleware_py39.py deleted file mode 100644 index a5da07ac6f..0000000000 --- a/tests/test_tutorial/test_sql_databases/test_sql_databases_middleware_py39.py +++ /dev/null @@ -1,433 +0,0 @@ -import importlib -import os -from pathlib import Path - -import pytest -from dirty_equals import IsDict -from fastapi.testclient import TestClient - -from ...utils import needs_py39, needs_pydanticv1 - - -@pytest.fixture(scope="module") -def client(tmp_path_factory: pytest.TempPathFactory): - tmp_path = tmp_path_factory.mktemp("data") - cwd = os.getcwd() - os.chdir(tmp_path) - test_db = Path("./sql_app.db") - if test_db.is_file(): # pragma: nocover - test_db.unlink() - # Import while creating the client to create the DB after starting the test session - from docs_src.sql_databases.sql_app_py39 import alt_main - - # Ensure import side effects are re-executed - importlib.reload(alt_main) - - with TestClient(alt_main.app) as c: - yield c - if test_db.is_file(): # pragma: nocover - test_db.unlink() - os.chdir(cwd) - - -@needs_py39 -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_create_user(client): - test_user = {"email": "johndoe@example.com", "password": "secret"} - response = client.post("/users/", json=test_user) - assert response.status_code == 200, response.text - data = response.json() - assert test_user["email"] == data["email"] - assert "id" in data - response = client.post("/users/", json=test_user) - assert response.status_code == 400, response.text - - -@needs_py39 -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_get_user(client): - response = client.get("/users/1") - assert response.status_code == 200, response.text - data = response.json() - assert "email" in data - assert "id" in data - - -@needs_py39 -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_nonexistent_user(client): - response = client.get("/users/999") - assert response.status_code == 404, response.text - - -@needs_py39 -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_get_users(client): - response = client.get("/users/") - assert response.status_code == 200, response.text - data = response.json() - assert "email" in data[0] - assert "id" in data[0] - - -@needs_py39 -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_create_item(client): - item = {"title": "Foo", "description": "Something that fights"} - response = client.post("/users/1/items/", json=item) - assert response.status_code == 200, response.text - item_data = response.json() - assert item["title"] == item_data["title"] - assert item["description"] == item_data["description"] - assert "id" in item_data - assert "owner_id" in item_data - response = client.get("/users/1") - assert response.status_code == 200, response.text - user_data = response.json() - item_to_check = [it for it in user_data["items"] if it["id"] == item_data["id"]][0] - assert item_to_check["title"] == item["title"] - assert item_to_check["description"] == item["description"] - response = client.get("/users/1") - assert response.status_code == 200, response.text - user_data = response.json() - item_to_check = [it for it in user_data["items"] if it["id"] == item_data["id"]][0] - assert item_to_check["title"] == item["title"] - assert item_to_check["description"] == item["description"] - - -@needs_py39 -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_read_items(client): - response = client.get("/items/") - assert response.status_code == 200, response.text - data = response.json() - assert data - first_item = data[0] - assert "title" in first_item - assert "description" in first_item - - -@needs_py39 -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_openapi_schema(client: TestClient): - 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": { - "/users/": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": { - "title": "Response Read Users Users Get", - "type": "array", - "items": {"$ref": "#/components/schemas/User"}, - } - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read Users", - "operationId": "read_users_users__get", - "parameters": [ - { - "required": False, - "schema": { - "title": "Skip", - "type": "integer", - "default": 0, - }, - "name": "skip", - "in": "query", - }, - { - "required": False, - "schema": { - "title": "Limit", - "type": "integer", - "default": 100, - }, - "name": "limit", - "in": "query", - }, - ], - }, - "post": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/User"} - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Create User", - "operationId": "create_user_users__post", - "requestBody": { - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/UserCreate"} - } - }, - "required": True, - }, - }, - }, - "/users/{user_id}": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/User"} - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read User", - "operationId": "read_user_users__user_id__get", - "parameters": [ - { - "required": True, - "schema": {"title": "User Id", "type": "integer"}, - "name": "user_id", - "in": "path", - } - ], - } - }, - "/users/{user_id}/items/": { - "post": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/Item"} - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Create Item For User", - "operationId": "create_item_for_user_users__user_id__items__post", - "parameters": [ - { - "required": True, - "schema": {"title": "User Id", "type": "integer"}, - "name": "user_id", - "in": "path", - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/ItemCreate"} - } - }, - "required": True, - }, - } - }, - "/items/": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": { - "title": "Response Read Items Items Get", - "type": "array", - "items": {"$ref": "#/components/schemas/Item"}, - } - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read Items", - "operationId": "read_items_items__get", - "parameters": [ - { - "required": False, - "schema": { - "title": "Skip", - "type": "integer", - "default": 0, - }, - "name": "skip", - "in": "query", - }, - { - "required": False, - "schema": { - "title": "Limit", - "type": "integer", - "default": 100, - }, - "name": "limit", - "in": "query", - }, - ], - } - }, - }, - "components": { - "schemas": { - "ItemCreate": { - "title": "ItemCreate", - "required": ["title"], - "type": "object", - "properties": { - "title": {"title": "Title", "type": "string"}, - "description": IsDict( - { - "title": "Description", - "anyOf": [{"type": "string"}, {"type": "null"}], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - {"title": "Description", "type": "string"} - ), - }, - }, - "Item": { - "title": "Item", - "required": ["title", "id", "owner_id"], - "type": "object", - "properties": { - "title": {"title": "Title", "type": "string"}, - "description": IsDict( - { - "title": "Description", - "anyOf": [{"type": "string"}, {"type": "null"}], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - {"title": "Description", "type": "string"}, - ), - "id": {"title": "Id", "type": "integer"}, - "owner_id": {"title": "Owner Id", "type": "integer"}, - }, - }, - "User": { - "title": "User", - "required": ["email", "id", "is_active"], - "type": "object", - "properties": { - "email": {"title": "Email", "type": "string"}, - "id": {"title": "Id", "type": "integer"}, - "is_active": {"title": "Is Active", "type": "boolean"}, - "items": { - "title": "Items", - "type": "array", - "items": {"$ref": "#/components/schemas/Item"}, - "default": [], - }, - }, - }, - "UserCreate": { - "title": "UserCreate", - "required": ["email", "password"], - "type": "object", - "properties": { - "email": {"title": "Email", "type": "string"}, - "password": {"title": "Password", "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"}, - }, - }, - "HTTPValidationError": { - "title": "HTTPValidationError", - "type": "object", - "properties": { - "detail": { - "title": "Detail", - "type": "array", - "items": {"$ref": "#/components/schemas/ValidationError"}, - } - }, - }, - } - }, - } diff --git a/tests/test_tutorial/test_sql_databases/test_sql_databases_py310.py b/tests/test_tutorial/test_sql_databases/test_sql_databases_py310.py deleted file mode 100644 index 5a91065988..0000000000 --- a/tests/test_tutorial/test_sql_databases/test_sql_databases_py310.py +++ /dev/null @@ -1,432 +0,0 @@ -import importlib -import os -from pathlib import Path - -import pytest -from dirty_equals import IsDict -from fastapi.testclient import TestClient - -from ...utils import needs_py310, needs_pydanticv1 - - -@pytest.fixture(scope="module", name="client") -def get_client(tmp_path_factory: pytest.TempPathFactory): - tmp_path = tmp_path_factory.mktemp("data") - cwd = os.getcwd() - os.chdir(tmp_path) - test_db = Path("./sql_app.db") - if test_db.is_file(): # pragma: nocover - test_db.unlink() - # Import while creating the client to create the DB after starting the test session - from docs_src.sql_databases.sql_app_py310 import main - - # Ensure import side effects are re-executed - importlib.reload(main) - with TestClient(main.app) as c: - yield c - if test_db.is_file(): # pragma: nocover - test_db.unlink() - os.chdir(cwd) - - -@needs_py310 -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_create_user(client): - test_user = {"email": "johndoe@example.com", "password": "secret"} - response = client.post("/users/", json=test_user) - assert response.status_code == 200, response.text - data = response.json() - assert test_user["email"] == data["email"] - assert "id" in data - response = client.post("/users/", json=test_user) - assert response.status_code == 400, response.text - - -@needs_py310 -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_get_user(client): - response = client.get("/users/1") - assert response.status_code == 200, response.text - data = response.json() - assert "email" in data - assert "id" in data - - -@needs_py310 -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_nonexistent_user(client): - response = client.get("/users/999") - assert response.status_code == 404, response.text - - -@needs_py310 -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_get_users(client): - response = client.get("/users/") - assert response.status_code == 200, response.text - data = response.json() - assert "email" in data[0] - assert "id" in data[0] - - -@needs_py310 -# TODO: pv2 add Pydantic v2 version -@needs_pydanticv1 -def test_create_item(client): - item = {"title": "Foo", "description": "Something that fights"} - response = client.post("/users/1/items/", json=item) - assert response.status_code == 200, response.text - item_data = response.json() - assert item["title"] == item_data["title"] - assert item["description"] == item_data["description"] - assert "id" in item_data - assert "owner_id" in item_data - response = client.get("/users/1") - assert response.status_code == 200, response.text - user_data = response.json() - item_to_check = [it for it in user_data["items"] if it["id"] == item_data["id"]][0] - assert item_to_check["title"] == item["title"] - assert item_to_check["description"] == item["description"] - response = client.get("/users/1") - assert response.status_code == 200, response.text - user_data = response.json() - item_to_check = [it for it in user_data["items"] if it["id"] == item_data["id"]][0] - assert item_to_check["title"] == item["title"] - assert item_to_check["description"] == item["description"] - - -@needs_py310 -# TODO: pv2 add Pydantic v2 version -@needs_pydanticv1 -def test_read_items(client): - response = client.get("/items/") - assert response.status_code == 200, response.text - data = response.json() - assert data - first_item = data[0] - assert "title" in first_item - assert "description" in first_item - - -@needs_py310 -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_openapi_schema(client: TestClient): - 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": { - "/users/": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": { - "title": "Response Read Users Users Get", - "type": "array", - "items": {"$ref": "#/components/schemas/User"}, - } - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read Users", - "operationId": "read_users_users__get", - "parameters": [ - { - "required": False, - "schema": { - "title": "Skip", - "type": "integer", - "default": 0, - }, - "name": "skip", - "in": "query", - }, - { - "required": False, - "schema": { - "title": "Limit", - "type": "integer", - "default": 100, - }, - "name": "limit", - "in": "query", - }, - ], - }, - "post": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/User"} - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Create User", - "operationId": "create_user_users__post", - "requestBody": { - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/UserCreate"} - } - }, - "required": True, - }, - }, - }, - "/users/{user_id}": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/User"} - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read User", - "operationId": "read_user_users__user_id__get", - "parameters": [ - { - "required": True, - "schema": {"title": "User Id", "type": "integer"}, - "name": "user_id", - "in": "path", - } - ], - } - }, - "/users/{user_id}/items/": { - "post": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/Item"} - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Create Item For User", - "operationId": "create_item_for_user_users__user_id__items__post", - "parameters": [ - { - "required": True, - "schema": {"title": "User Id", "type": "integer"}, - "name": "user_id", - "in": "path", - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/ItemCreate"} - } - }, - "required": True, - }, - } - }, - "/items/": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": { - "title": "Response Read Items Items Get", - "type": "array", - "items": {"$ref": "#/components/schemas/Item"}, - } - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read Items", - "operationId": "read_items_items__get", - "parameters": [ - { - "required": False, - "schema": { - "title": "Skip", - "type": "integer", - "default": 0, - }, - "name": "skip", - "in": "query", - }, - { - "required": False, - "schema": { - "title": "Limit", - "type": "integer", - "default": 100, - }, - "name": "limit", - "in": "query", - }, - ], - } - }, - }, - "components": { - "schemas": { - "ItemCreate": { - "title": "ItemCreate", - "required": ["title"], - "type": "object", - "properties": { - "title": {"title": "Title", "type": "string"}, - "description": IsDict( - { - "title": "Description", - "anyOf": [{"type": "string"}, {"type": "null"}], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - {"title": "Description", "type": "string"} - ), - }, - }, - "Item": { - "title": "Item", - "required": ["title", "id", "owner_id"], - "type": "object", - "properties": { - "title": {"title": "Title", "type": "string"}, - "description": IsDict( - { - "title": "Description", - "anyOf": [{"type": "string"}, {"type": "null"}], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - {"title": "Description", "type": "string"}, - ), - "id": {"title": "Id", "type": "integer"}, - "owner_id": {"title": "Owner Id", "type": "integer"}, - }, - }, - "User": { - "title": "User", - "required": ["email", "id", "is_active"], - "type": "object", - "properties": { - "email": {"title": "Email", "type": "string"}, - "id": {"title": "Id", "type": "integer"}, - "is_active": {"title": "Is Active", "type": "boolean"}, - "items": { - "title": "Items", - "type": "array", - "items": {"$ref": "#/components/schemas/Item"}, - "default": [], - }, - }, - }, - "UserCreate": { - "title": "UserCreate", - "required": ["email", "password"], - "type": "object", - "properties": { - "email": {"title": "Email", "type": "string"}, - "password": {"title": "Password", "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"}, - }, - }, - "HTTPValidationError": { - "title": "HTTPValidationError", - "type": "object", - "properties": { - "detail": { - "title": "Detail", - "type": "array", - "items": {"$ref": "#/components/schemas/ValidationError"}, - } - }, - }, - } - }, - } diff --git a/tests/test_tutorial/test_sql_databases/test_sql_databases_py39.py b/tests/test_tutorial/test_sql_databases/test_sql_databases_py39.py deleted file mode 100644 index a354ba9053..0000000000 --- a/tests/test_tutorial/test_sql_databases/test_sql_databases_py39.py +++ /dev/null @@ -1,432 +0,0 @@ -import importlib -import os -from pathlib import Path - -import pytest -from dirty_equals import IsDict -from fastapi.testclient import TestClient - -from ...utils import needs_py39, needs_pydanticv1 - - -@pytest.fixture(scope="module", name="client") -def get_client(tmp_path_factory: pytest.TempPathFactory): - tmp_path = tmp_path_factory.mktemp("data") - cwd = os.getcwd() - os.chdir(tmp_path) - test_db = Path("./sql_app.db") - if test_db.is_file(): # pragma: nocover - test_db.unlink() - # Import while creating the client to create the DB after starting the test session - from docs_src.sql_databases.sql_app_py39 import main - - # Ensure import side effects are re-executed - importlib.reload(main) - with TestClient(main.app) as c: - yield c - if test_db.is_file(): # pragma: nocover - test_db.unlink() - os.chdir(cwd) - - -@needs_py39 -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_create_user(client): - test_user = {"email": "johndoe@example.com", "password": "secret"} - response = client.post("/users/", json=test_user) - assert response.status_code == 200, response.text - data = response.json() - assert test_user["email"] == data["email"] - assert "id" in data - response = client.post("/users/", json=test_user) - assert response.status_code == 400, response.text - - -@needs_py39 -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_get_user(client): - response = client.get("/users/1") - assert response.status_code == 200, response.text - data = response.json() - assert "email" in data - assert "id" in data - - -@needs_py39 -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_nonexistent_user(client): - response = client.get("/users/999") - assert response.status_code == 404, response.text - - -@needs_py39 -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_get_users(client): - response = client.get("/users/") - assert response.status_code == 200, response.text - data = response.json() - assert "email" in data[0] - assert "id" in data[0] - - -@needs_py39 -# TODO: pv2 add Pydantic v2 version -@needs_pydanticv1 -def test_create_item(client): - item = {"title": "Foo", "description": "Something that fights"} - response = client.post("/users/1/items/", json=item) - assert response.status_code == 200, response.text - item_data = response.json() - assert item["title"] == item_data["title"] - assert item["description"] == item_data["description"] - assert "id" in item_data - assert "owner_id" in item_data - response = client.get("/users/1") - assert response.status_code == 200, response.text - user_data = response.json() - item_to_check = [it for it in user_data["items"] if it["id"] == item_data["id"]][0] - assert item_to_check["title"] == item["title"] - assert item_to_check["description"] == item["description"] - response = client.get("/users/1") - assert response.status_code == 200, response.text - user_data = response.json() - item_to_check = [it for it in user_data["items"] if it["id"] == item_data["id"]][0] - assert item_to_check["title"] == item["title"] - assert item_to_check["description"] == item["description"] - - -@needs_py39 -# TODO: pv2 add Pydantic v2 version -@needs_pydanticv1 -def test_read_items(client): - response = client.get("/items/") - assert response.status_code == 200, response.text - data = response.json() - assert data - first_item = data[0] - assert "title" in first_item - assert "description" in first_item - - -@needs_py39 -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_openapi_schema(client: TestClient): - 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": { - "/users/": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": { - "title": "Response Read Users Users Get", - "type": "array", - "items": {"$ref": "#/components/schemas/User"}, - } - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read Users", - "operationId": "read_users_users__get", - "parameters": [ - { - "required": False, - "schema": { - "title": "Skip", - "type": "integer", - "default": 0, - }, - "name": "skip", - "in": "query", - }, - { - "required": False, - "schema": { - "title": "Limit", - "type": "integer", - "default": 100, - }, - "name": "limit", - "in": "query", - }, - ], - }, - "post": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/User"} - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Create User", - "operationId": "create_user_users__post", - "requestBody": { - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/UserCreate"} - } - }, - "required": True, - }, - }, - }, - "/users/{user_id}": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/User"} - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read User", - "operationId": "read_user_users__user_id__get", - "parameters": [ - { - "required": True, - "schema": {"title": "User Id", "type": "integer"}, - "name": "user_id", - "in": "path", - } - ], - } - }, - "/users/{user_id}/items/": { - "post": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/Item"} - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Create Item For User", - "operationId": "create_item_for_user_users__user_id__items__post", - "parameters": [ - { - "required": True, - "schema": {"title": "User Id", "type": "integer"}, - "name": "user_id", - "in": "path", - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": {"$ref": "#/components/schemas/ItemCreate"} - } - }, - "required": True, - }, - } - }, - "/items/": { - "get": { - "responses": { - "200": { - "description": "Successful Response", - "content": { - "application/json": { - "schema": { - "title": "Response Read Items Items Get", - "type": "array", - "items": {"$ref": "#/components/schemas/Item"}, - } - } - }, - }, - "422": { - "description": "Validation Error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/HTTPValidationError" - } - } - }, - }, - }, - "summary": "Read Items", - "operationId": "read_items_items__get", - "parameters": [ - { - "required": False, - "schema": { - "title": "Skip", - "type": "integer", - "default": 0, - }, - "name": "skip", - "in": "query", - }, - { - "required": False, - "schema": { - "title": "Limit", - "type": "integer", - "default": 100, - }, - "name": "limit", - "in": "query", - }, - ], - } - }, - }, - "components": { - "schemas": { - "ItemCreate": { - "title": "ItemCreate", - "required": ["title"], - "type": "object", - "properties": { - "title": {"title": "Title", "type": "string"}, - "description": IsDict( - { - "title": "Description", - "anyOf": [{"type": "string"}, {"type": "null"}], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - {"title": "Description", "type": "string"} - ), - }, - }, - "Item": { - "title": "Item", - "required": ["title", "id", "owner_id"], - "type": "object", - "properties": { - "title": {"title": "Title", "type": "string"}, - "description": IsDict( - { - "title": "Description", - "anyOf": [{"type": "string"}, {"type": "null"}], - } - ) - | IsDict( - # TODO: remove when deprecating Pydantic v1 - {"title": "Description", "type": "string"}, - ), - "id": {"title": "Id", "type": "integer"}, - "owner_id": {"title": "Owner Id", "type": "integer"}, - }, - }, - "User": { - "title": "User", - "required": ["email", "id", "is_active"], - "type": "object", - "properties": { - "email": {"title": "Email", "type": "string"}, - "id": {"title": "Id", "type": "integer"}, - "is_active": {"title": "Is Active", "type": "boolean"}, - "items": { - "title": "Items", - "type": "array", - "items": {"$ref": "#/components/schemas/Item"}, - "default": [], - }, - }, - }, - "UserCreate": { - "title": "UserCreate", - "required": ["email", "password"], - "type": "object", - "properties": { - "email": {"title": "Email", "type": "string"}, - "password": {"title": "Password", "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"}, - }, - }, - "HTTPValidationError": { - "title": "HTTPValidationError", - "type": "object", - "properties": { - "detail": { - "title": "Detail", - "type": "array", - "items": {"$ref": "#/components/schemas/ValidationError"}, - } - }, - }, - } - }, - } diff --git a/tests/test_tutorial/test_sql_databases/test_testing_databases.py b/tests/test_tutorial/test_sql_databases/test_testing_databases.py deleted file mode 100644 index ce6ce230c8..0000000000 --- a/tests/test_tutorial/test_sql_databases/test_testing_databases.py +++ /dev/null @@ -1,27 +0,0 @@ -import importlib -import os -from pathlib import Path - -import pytest - -from ...utils import needs_pydanticv1 - - -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_testing_dbs(tmp_path_factory: pytest.TempPathFactory): - tmp_path = tmp_path_factory.mktemp("data") - cwd = os.getcwd() - os.chdir(tmp_path) - test_db = Path("./test.db") - if test_db.is_file(): # pragma: nocover - test_db.unlink() - # Import while creating the client to create the DB after starting the test session - from docs_src.sql_databases.sql_app.tests import test_sql_app - - # Ensure import side effects are re-executed - importlib.reload(test_sql_app) - test_sql_app.test_create_user() - if test_db.is_file(): # pragma: nocover - test_db.unlink() - os.chdir(cwd) diff --git a/tests/test_tutorial/test_sql_databases/test_testing_databases_py310.py b/tests/test_tutorial/test_sql_databases/test_testing_databases_py310.py deleted file mode 100644 index 545d63c2a8..0000000000 --- a/tests/test_tutorial/test_sql_databases/test_testing_databases_py310.py +++ /dev/null @@ -1,28 +0,0 @@ -import importlib -import os -from pathlib import Path - -import pytest - -from ...utils import needs_py310, needs_pydanticv1 - - -@needs_py310 -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_testing_dbs_py39(tmp_path_factory: pytest.TempPathFactory): - tmp_path = tmp_path_factory.mktemp("data") - cwd = os.getcwd() - os.chdir(tmp_path) - test_db = Path("./test.db") - if test_db.is_file(): # pragma: nocover - test_db.unlink() - # Import while creating the client to create the DB after starting the test session - from docs_src.sql_databases.sql_app_py310.tests import test_sql_app - - # Ensure import side effects are re-executed - importlib.reload(test_sql_app) - test_sql_app.test_create_user() - if test_db.is_file(): # pragma: nocover - test_db.unlink() - os.chdir(cwd) diff --git a/tests/test_tutorial/test_sql_databases/test_testing_databases_py39.py b/tests/test_tutorial/test_sql_databases/test_testing_databases_py39.py deleted file mode 100644 index 99bfd3fa8a..0000000000 --- a/tests/test_tutorial/test_sql_databases/test_testing_databases_py39.py +++ /dev/null @@ -1,28 +0,0 @@ -import importlib -import os -from pathlib import Path - -import pytest - -from ...utils import needs_py39, needs_pydanticv1 - - -@needs_py39 -# TODO: pv2 add version with Pydantic v2 -@needs_pydanticv1 -def test_testing_dbs_py39(tmp_path_factory: pytest.TempPathFactory): - tmp_path = tmp_path_factory.mktemp("data") - cwd = os.getcwd() - os.chdir(tmp_path) - test_db = Path("./test.db") - if test_db.is_file(): # pragma: nocover - test_db.unlink() - # Import while creating the client to create the DB after starting the test session - from docs_src.sql_databases.sql_app_py39.tests import test_sql_app - - # Ensure import side effects are re-executed - importlib.reload(test_sql_app) - test_sql_app.test_create_user() - if test_db.is_file(): # pragma: nocover - test_db.unlink() - os.chdir(cwd) diff --git a/tests/test_tutorial/test_sql_databases/test_tutorial001.py b/tests/test_tutorial/test_sql_databases/test_tutorial001.py new file mode 100644 index 0000000000..cc7e590df0 --- /dev/null +++ b/tests/test_tutorial/test_sql_databases/test_tutorial001.py @@ -0,0 +1,373 @@ +import importlib +import warnings + +import pytest +from dirty_equals import IsDict, IsInt +from fastapi.testclient import TestClient +from inline_snapshot import snapshot +from sqlalchemy import StaticPool +from sqlmodel import SQLModel, create_engine +from sqlmodel.main import default_registry + +from tests.utils import needs_py39, needs_py310 + + +def clear_sqlmodel(): + # Clear the tables in the metadata for the default base model + SQLModel.metadata.clear() + # Clear the Models associated with the registry, to avoid warnings + default_registry.dispose() + + +@pytest.fixture( + name="client", + params=[ + "tutorial001", + pytest.param("tutorial001_py39", marks=needs_py39), + pytest.param("tutorial001_py310", marks=needs_py310), + "tutorial001_an", + pytest.param("tutorial001_an_py39", marks=needs_py39), + pytest.param("tutorial001_an_py310", marks=needs_py310), + ], +) +def get_client(request: pytest.FixtureRequest): + clear_sqlmodel() + # TODO: remove when updating SQL tutorial to use new lifespan API + with warnings.catch_warnings(record=True): + warnings.simplefilter("always") + mod = importlib.import_module(f"docs_src.sql_databases.{request.param}") + clear_sqlmodel() + importlib.reload(mod) + mod.sqlite_url = "sqlite://" + mod.engine = create_engine( + mod.sqlite_url, connect_args={"check_same_thread": False}, poolclass=StaticPool + ) + + with TestClient(mod.app) as c: + yield c + + +def test_crud_app(client: TestClient): + # TODO: this warns that SQLModel.from_orm is deprecated in Pydantic v1, refactor + # this if using obj.model_validate becomes independent of Pydantic v2 + with warnings.catch_warnings(record=True): + warnings.simplefilter("always") + # No heroes before creating + response = client.get("heroes/") + assert response.status_code == 200, response.text + assert response.json() == [] + + # Create a hero + response = client.post( + "/heroes/", + json={ + "id": 999, + "name": "Dead Pond", + "age": 30, + "secret_name": "Dive Wilson", + }, + ) + assert response.status_code == 200, response.text + assert response.json() == snapshot( + {"age": 30, "secret_name": "Dive Wilson", "id": 999, "name": "Dead Pond"} + ) + + # Read a hero + hero_id = response.json()["id"] + response = client.get(f"/heroes/{hero_id}") + assert response.status_code == 200, response.text + assert response.json() == snapshot( + {"name": "Dead Pond", "age": 30, "id": 999, "secret_name": "Dive Wilson"} + ) + + # Read all heroes + # Create more heroes first + response = client.post( + "/heroes/", + json={"name": "Spider-Boy", "age": 18, "secret_name": "Pedro Parqueador"}, + ) + assert response.status_code == 200, response.text + response = client.post( + "/heroes/", json={"name": "Rusty-Man", "secret_name": "Tommy Sharp"} + ) + assert response.status_code == 200, response.text + + response = client.get("/heroes/") + assert response.status_code == 200, response.text + assert response.json() == snapshot( + [ + { + "name": "Dead Pond", + "age": 30, + "id": IsInt(), + "secret_name": "Dive Wilson", + }, + { + "name": "Spider-Boy", + "age": 18, + "id": IsInt(), + "secret_name": "Pedro Parqueador", + }, + { + "name": "Rusty-Man", + "age": None, + "id": IsInt(), + "secret_name": "Tommy Sharp", + }, + ] + ) + + response = client.get("/heroes/?offset=1&limit=1") + assert response.status_code == 200, response.text + assert response.json() == snapshot( + [ + { + "name": "Spider-Boy", + "age": 18, + "id": IsInt(), + "secret_name": "Pedro Parqueador", + } + ] + ) + + # Delete a hero + response = client.delete(f"/heroes/{hero_id}") + assert response.status_code == 200, response.text + assert response.json() == snapshot({"ok": True}) + + response = client.get(f"/heroes/{hero_id}") + assert response.status_code == 404, response.text + + response = client.delete(f"/heroes/{hero_id}") + assert response.status_code == 404, response.text + assert response.json() == snapshot({"detail": "Hero not found"}) + + +def test_openapi_schema(client: TestClient): + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == snapshot( + { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/heroes/": { + "post": { + "summary": "Create Hero", + "operationId": "create_hero_heroes__post", + "requestBody": { + "required": True, + "content": { + "application/json": { + "schema": {"$ref": "#/components/schemas/Hero"} + } + }, + }, + "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" + } + } + }, + }, + }, + }, + "get": { + "summary": "Read Heroes", + "operationId": "read_heroes_heroes__get", + "parameters": [ + { + "name": "offset", + "in": "query", + "required": False, + "schema": { + "type": "integer", + "default": 0, + "title": "Offset", + }, + }, + { + "name": "limit", + "in": "query", + "required": False, + "schema": { + "type": "integer", + "maximum": 100, + "default": 100, + "title": "Limit", + }, + }, + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Hero" + }, + "title": "Response Read Heroes Heroes Get", + } + } + }, + }, + "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": [ + { + "name": "hero_id", + "in": "path", + "required": True, + "schema": {"type": "integer", "title": "Hero Id"}, + } + ], + "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" + } + } + }, + }, + }, + }, + "delete": { + "summary": "Delete Hero", + "operationId": "delete_hero_heroes__hero_id__delete", + "parameters": [ + { + "name": "hero_id", + "in": "path", + "required": True, + "schema": {"type": "integer", "title": "Hero Id"}, + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + }, + "components": { + "schemas": { + "HTTPValidationError": { + "properties": { + "detail": { + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + "type": "array", + "title": "Detail", + } + }, + "type": "object", + "title": "HTTPValidationError", + }, + "Hero": { + "properties": { + "id": IsDict( + { + "anyOf": [{"type": "integer"}, {"type": "null"}], + "title": "Id", + } + ) + | IsDict( + # TODO: remove when deprecating Pydantic v1 + { + "type": "integer", + "title": "Id", + } + ), + "name": {"type": "string", "title": "Name"}, + "age": IsDict( + { + "anyOf": [{"type": "integer"}, {"type": "null"}], + "title": "Age", + } + ) + | IsDict( + # TODO: remove when deprecating Pydantic v1 + { + "type": "integer", + "title": "Age", + } + ), + "secret_name": {"type": "string", "title": "Secret Name"}, + }, + "type": "object", + "required": ["name", "secret_name"], + "title": "Hero", + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + "type": "array", + "title": "Location", + }, + "msg": {"type": "string", "title": "Message"}, + "type": {"type": "string", "title": "Error Type"}, + }, + "type": "object", + "required": ["loc", "msg", "type"], + "title": "ValidationError", + }, + } + }, + } + ) diff --git a/tests/test_tutorial/test_sql_databases/test_tutorial002.py b/tests/test_tutorial/test_sql_databases/test_tutorial002.py new file mode 100644 index 0000000000..68c1966f55 --- /dev/null +++ b/tests/test_tutorial/test_sql_databases/test_tutorial002.py @@ -0,0 +1,481 @@ +import importlib +import warnings + +import pytest +from dirty_equals import IsDict, IsInt +from fastapi.testclient import TestClient +from inline_snapshot import snapshot +from sqlalchemy import StaticPool +from sqlmodel import SQLModel, create_engine +from sqlmodel.main import default_registry + +from tests.utils import needs_py39, needs_py310 + + +def clear_sqlmodel(): + # Clear the tables in the metadata for the default base model + SQLModel.metadata.clear() + # Clear the Models associated with the registry, to avoid warnings + default_registry.dispose() + + +@pytest.fixture( + name="client", + params=[ + "tutorial002", + pytest.param("tutorial002_py39", marks=needs_py39), + pytest.param("tutorial002_py310", marks=needs_py310), + "tutorial002_an", + pytest.param("tutorial002_an_py39", marks=needs_py39), + pytest.param("tutorial002_an_py310", marks=needs_py310), + ], +) +def get_client(request: pytest.FixtureRequest): + clear_sqlmodel() + # TODO: remove when updating SQL tutorial to use new lifespan API + with warnings.catch_warnings(record=True): + warnings.simplefilter("always") + mod = importlib.import_module(f"docs_src.sql_databases.{request.param}") + clear_sqlmodel() + importlib.reload(mod) + mod.sqlite_url = "sqlite://" + mod.engine = create_engine( + mod.sqlite_url, connect_args={"check_same_thread": False}, poolclass=StaticPool + ) + + with TestClient(mod.app) as c: + yield c + + +def test_crud_app(client: TestClient): + # TODO: this warns that SQLModel.from_orm is deprecated in Pydantic v1, refactor + # this if using obj.model_validate becomes independent of Pydantic v2 + with warnings.catch_warnings(record=True): + warnings.simplefilter("always") + # No heroes before creating + response = client.get("heroes/") + assert response.status_code == 200, response.text + assert response.json() == [] + + # Create a hero + response = client.post( + "/heroes/", + json={ + "id": 9000, + "name": "Dead Pond", + "age": 30, + "secret_name": "Dive Wilson", + }, + ) + assert response.status_code == 200, response.text + assert response.json() == snapshot( + {"age": 30, "id": IsInt(), "name": "Dead Pond"} + ) + assert ( + response.json()["id"] != 9000 + ), "The ID should be generated by the database" + + # Read a hero + hero_id = response.json()["id"] + response = client.get(f"/heroes/{hero_id}") + assert response.status_code == 200, response.text + assert response.json() == snapshot( + {"name": "Dead Pond", "age": 30, "id": IsInt()} + ) + + # Read all heroes + # Create more heroes first + response = client.post( + "/heroes/", + json={"name": "Spider-Boy", "age": 18, "secret_name": "Pedro Parqueador"}, + ) + assert response.status_code == 200, response.text + response = client.post( + "/heroes/", json={"name": "Rusty-Man", "secret_name": "Tommy Sharp"} + ) + assert response.status_code == 200, response.text + + response = client.get("/heroes/") + assert response.status_code == 200, response.text + assert response.json() == snapshot( + [ + {"name": "Dead Pond", "age": 30, "id": IsInt()}, + {"name": "Spider-Boy", "age": 18, "id": IsInt()}, + {"name": "Rusty-Man", "age": None, "id": IsInt()}, + ] + ) + + response = client.get("/heroes/?offset=1&limit=1") + assert response.status_code == 200, response.text + assert response.json() == snapshot( + [{"name": "Spider-Boy", "age": 18, "id": IsInt()}] + ) + + # Update a hero + response = client.patch( + f"/heroes/{hero_id}", json={"name": "Dog Pond", "age": None} + ) + assert response.status_code == 200, response.text + assert response.json() == snapshot( + {"name": "Dog Pond", "age": None, "id": hero_id} + ) + + # Get updated hero + response = client.get(f"/heroes/{hero_id}") + assert response.status_code == 200, response.text + assert response.json() == snapshot( + {"name": "Dog Pond", "age": None, "id": hero_id} + ) + + # Delete a hero + response = client.delete(f"/heroes/{hero_id}") + assert response.status_code == 200, response.text + assert response.json() == snapshot({"ok": True}) + + # The hero is no longer found + response = client.get(f"/heroes/{hero_id}") + assert response.status_code == 404, response.text + + # Delete a hero that does not exist + response = client.delete(f"/heroes/{hero_id}") + assert response.status_code == 404, response.text + assert response.json() == snapshot({"detail": "Hero not found"}) + + # Update a hero that does not exist + response = client.patch(f"/heroes/{hero_id}", json={"name": "Dog Pond"}) + assert response.status_code == 404, response.text + assert response.json() == snapshot({"detail": "Hero not found"}) + + +def test_openapi_schema(client: TestClient): + response = client.get("/openapi.json") + assert response.status_code == 200, response.text + assert response.json() == snapshot( + { + "openapi": "3.1.0", + "info": {"title": "FastAPI", "version": "0.1.0"}, + "paths": { + "/heroes/": { + "post": { + "summary": "Create Hero", + "operationId": "create_hero_heroes__post", + "requestBody": { + "required": True, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroCreate" + } + } + }, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroPublic" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "get": { + "summary": "Read Heroes", + "operationId": "read_heroes_heroes__get", + "parameters": [ + { + "name": "offset", + "in": "query", + "required": False, + "schema": { + "type": "integer", + "default": 0, + "title": "Offset", + }, + }, + { + "name": "limit", + "in": "query", + "required": False, + "schema": { + "type": "integer", + "maximum": 100, + "default": 100, + "title": "Limit", + }, + }, + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/HeroPublic" + }, + "title": "Response Read Heroes Heroes Get", + } + } + }, + }, + "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": [ + { + "name": "hero_id", + "in": "path", + "required": True, + "schema": {"type": "integer", "title": "Hero Id"}, + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroPublic" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "patch": { + "summary": "Update Hero", + "operationId": "update_hero_heroes__hero_id__patch", + "parameters": [ + { + "name": "hero_id", + "in": "path", + "required": True, + "schema": {"type": "integer", "title": "Hero Id"}, + } + ], + "requestBody": { + "required": True, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroUpdate" + } + } + }, + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HeroPublic" + } + } + }, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + "delete": { + "summary": "Delete Hero", + "operationId": "delete_hero_heroes__hero_id__delete", + "parameters": [ + { + "name": "hero_id", + "in": "path", + "required": True, + "schema": {"type": "integer", "title": "Hero Id"}, + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": {"application/json": {"schema": {}}}, + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HTTPValidationError" + } + } + }, + }, + }, + }, + }, + }, + "components": { + "schemas": { + "HTTPValidationError": { + "properties": { + "detail": { + "items": { + "$ref": "#/components/schemas/ValidationError" + }, + "type": "array", + "title": "Detail", + } + }, + "type": "object", + "title": "HTTPValidationError", + }, + "HeroCreate": { + "properties": { + "name": {"type": "string", "title": "Name"}, + "age": IsDict( + { + "anyOf": [{"type": "integer"}, {"type": "null"}], + "title": "Age", + } + ) + | IsDict( + # TODO: remove when deprecating Pydantic v1 + { + "type": "integer", + "title": "Age", + } + ), + "secret_name": {"type": "string", "title": "Secret Name"}, + }, + "type": "object", + "required": ["name", "secret_name"], + "title": "HeroCreate", + }, + "HeroPublic": { + "properties": { + "name": {"type": "string", "title": "Name"}, + "age": IsDict( + { + "anyOf": [{"type": "integer"}, {"type": "null"}], + "title": "Age", + } + ) + | IsDict( + # TODO: remove when deprecating Pydantic v1 + { + "type": "integer", + "title": "Age", + } + ), + "id": {"type": "integer", "title": "Id"}, + }, + "type": "object", + "required": ["name", "id"], + "title": "HeroPublic", + }, + "HeroUpdate": { + "properties": { + "name": IsDict( + { + "anyOf": [{"type": "string"}, {"type": "null"}], + "title": "Name", + } + ) + | IsDict( + # TODO: remove when deprecating Pydantic v1 + { + "type": "string", + "title": "Name", + } + ), + "age": IsDict( + { + "anyOf": [{"type": "integer"}, {"type": "null"}], + "title": "Age", + } + ) + | IsDict( + # TODO: remove when deprecating Pydantic v1 + { + "type": "integer", + "title": "Age", + } + ), + "secret_name": IsDict( + { + "anyOf": [{"type": "string"}, {"type": "null"}], + "title": "Secret Name", + } + ) + | IsDict( + # TODO: remove when deprecating Pydantic v1 + { + "type": "string", + "title": "Secret Name", + } + ), + }, + "type": "object", + "title": "HeroUpdate", + }, + "ValidationError": { + "properties": { + "loc": { + "items": { + "anyOf": [{"type": "string"}, {"type": "integer"}] + }, + "type": "array", + "title": "Location", + }, + "msg": {"type": "string", "title": "Message"}, + "type": {"type": "string", "title": "Error Type"}, + }, + "type": "object", + "required": ["loc", "msg", "type"], + "title": "ValidationError", + }, + } + }, + } + ) -- 2.47.3