Compare commits

..

277 Commits

Author SHA1 Message Date
Sebastián Ramírez
f3f31a9698 📝 Update release notes 2024-07-20 21:12:48 -05:00
Sebastián Ramírez
458e088170 🔖 Release version 0.0.21 2024-07-20 21:10:30 -05:00
github-actions
49735a1c93 📝 Update release notes 2024-07-21 02:08:28 +00:00
Esteban Maya
86ab09f7ec Add support for cascade delete relationships: cascade_delete, ondelete, and passive_deletes (#983)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2024-07-20 21:08:06 -05:00
github-actions
19c736766e 📝 Update release notes 2024-07-17 23:52:33 +00:00
dependabot[bot]
b8718c3b4d ⬆ Bump actions/cache from 3 to 4 (#783)
Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-17 18:52:15 -05:00
github-actions
179b7781c2 📝 Update release notes 2024-07-17 03:18:45 +00:00
dependabot[bot]
54d8a685cc ⬆ Bump cairosvg from 2.7.0 to 2.7.1 (#919)
Bumps [cairosvg](https://github.com/Kozea/CairoSVG) from 2.7.0 to 2.7.1.
- [Release notes](https://github.com/Kozea/CairoSVG/releases)
- [Changelog](https://github.com/Kozea/CairoSVG/blob/main/NEWS.rst)
- [Commits](https://github.com/Kozea/CairoSVG/compare/2.7.0...2.7.1)

---
updated-dependencies:
- dependency-name: cairosvg
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-17 03:18:16 +00:00
github-actions
eb5fb78114 📝 Update release notes 2024-07-17 03:16:06 +00:00
dependabot[bot]
eaa9023049 ⬆ Bump jinja2 from 3.1.3 to 3.1.4 (#974)
Bumps [jinja2](https://github.com/pallets/jinja) from 3.1.3 to 3.1.4.
- [Release notes](https://github.com/pallets/jinja/releases)
- [Changelog](https://github.com/pallets/jinja/blob/main/CHANGES.rst)
- [Commits](https://github.com/pallets/jinja/compare/3.1.3...3.1.4)

---
updated-dependencies:
- dependency-name: jinja2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-17 03:15:45 +00:00
github-actions
50b16972fe 📝 Update release notes 2024-07-17 03:10:35 +00:00
github-actions
814d3a4b14 📝 Update release notes 2024-07-17 03:09:30 +00:00
dependabot[bot]
58d9d2c3a6 ⬆ Bump pypa/gh-action-pypi-publish from 1.8.11 to 1.9.0 (#987)
Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.8.11 to 1.9.0.
- [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases)
- [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/v1.8.11...v1.9.0)

---
updated-dependencies:
- dependency-name: pypa/gh-action-pypi-publish
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-16 22:08:25 -05:00
dependabot[bot]
eb92b8315d ⬆ Bump mkdocstrings[python] from 0.23.0 to 0.25.1 (#927)
Bumps [mkdocstrings[python]](https://github.com/mkdocstrings/mkdocstrings) from 0.23.0 to 0.25.1.
- [Release notes](https://github.com/mkdocstrings/mkdocstrings/releases)
- [Changelog](https://github.com/mkdocstrings/mkdocstrings/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mkdocstrings/mkdocstrings/compare/0.23.0...0.25.1)

---
updated-dependencies:
- dependency-name: mkdocstrings[python]
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-16 22:07:28 -05:00
github-actions
8792c442d7 📝 Update release notes 2024-07-17 03:07:13 +00:00
dependabot[bot]
a23e36bec9 ⬆ Bump dorny/paths-filter from 2 to 3 (#972)
Bumps [dorny/paths-filter](https://github.com/dorny/paths-filter) from 2 to 3.
- [Release notes](https://github.com/dorny/paths-filter/releases)
- [Changelog](https://github.com/dorny/paths-filter/blob/master/CHANGELOG.md)
- [Commits](https://github.com/dorny/paths-filter/compare/v2...v3)

---
updated-dependencies:
- dependency-name: dorny/paths-filter
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-16 22:06:53 -05:00
github-actions
3c75056c6e 📝 Update release notes 2024-07-17 03:04:11 +00:00
Alejandra
da1253c21f 📝 Update docs (#1003) 2024-07-16 22:03:52 -05:00
Sebastián Ramírez
b8d7f4ff67 🔖 Release version 0.0.20 2024-07-16 21:53:24 -05:00
Sebastián Ramírez
438480f128 📝 Update release notes 2024-07-16 21:52:43 -05:00
github-actions
7ba80e47e7 📝 Update release notes 2024-07-17 02:21:19 +00:00
pre-commit-ci[bot]
1920f07052 ⬆ [pre-commit.ci] pre-commit autoupdate (#979)
updates:
- [github.com/pre-commit/pre-commit-hooks: v4.5.0 → v4.6.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.5.0...v4.6.0)
- [github.com/astral-sh/ruff-pre-commit: v0.4.7 → v0.5.2](https://github.com/astral-sh/ruff-pre-commit/compare/v0.4.7...v0.5.2)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-07-16 21:21:01 -05:00
github-actions
09adc76e3d 📝 Update release notes 2024-07-17 02:16:15 +00:00
Sebastián Ramírez
690f9cf5e1 🔨 Update docs Termynal scripts to not include line nums for local dev (#1018) 2024-07-17 02:15:42 +00:00
github-actions
fca0621098 📝 Update release notes 2024-07-17 01:52:20 +00:00
Esteban Maya
95936bb508 Add official UUID support, docs and tests, internally using new SQLAlchemy 2.0 types (#992)
*  Add UUID support from sqlalchemy 2.0 update

* ⚰️ Remove dead code for GUID old support

* 📝 Add documentation for UUIDs

* 🧪 Add test for UUIDs field definition and support

* 🎨 [pre-commit.ci] Auto format from pre-commit.com hooks

* ✏️ Fix prerequisites docs for uuid

* ♻️ Update UUID source examples for consistency

Keep consistency with other examples, functions without parameters, and printing info that shows and explains the UUID results (and can also be tested later)

* 📝 Add source examples for selecting UUIDs with session.get()

* 📝 Re-structure UUID docs

* Explain the concepts at the beggining before using them.
* Explain how UUIDs can be used and trusted.
* Explain why UUIDs could be generated on the code, and how they can be used for distributed systems.
* Explain how UUIDs can prevent information leakage.
* Warn about UUIDs storage size.
* Explain that uuid is part of the standard library.
* Explain how default_factory works.
* Explain that creating an instance would generate a new UUID, before it is sent to the DB. This is included and shown in the example, the UUID is printed before saving to the DB.
* Remove sections about other operations that would behave the same as other fields and don't need additional info from what was explained in previous chapters.
* Add two examples to select using UUIDs, similar to the previous ones, mainly to be able to use them in the tests and ensure that it all works, even when SQLite stores the values as strings but the where() or the session.get() receive UUID values (ensure SQLAlchemy does the conversion correctly for SQLite).
* Add an example terminal run of the code, with comments.
* Simplify the ending to keep only the information that wasn't there before, just the "Learn More" with links.

*  Refactor tests with new printed code, extract and check that UUIDs are used in the right places.

*  Add tests for the new extra UUID examples, for session.get()

* 🎨 [pre-commit.ci] Auto format from pre-commit.com hooks

* 📝 Rename variable in example for Python 3.7+ for consistency with 3.10+ (I missed that change before)

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2024-07-16 20:52:03 -05:00
github-actions
3b889e09f7 📝 Update release notes 2024-06-21 02:17:19 +00:00
Toby Penner
600da0a25c ✏️ Fix internal link in docs/tutorial/create-db-and-table.md (#911) 2024-06-20 21:16:56 -05:00
github-actions
96bfd855f8 📝 Update release notes 2024-06-05 01:52:54 +00:00
Alejandra
8416508d79 ✏️ Add missing step in create-db-and-table-with-db-browser.md (#976) 2024-06-05 01:52:36 +00:00
github-actions
f1bfebc9e2 📝 Update release notes 2024-06-05 00:00:30 +00:00
Lucien O
1263024be5 ✏️ Fix typo in docs/tutorial (#943) 2024-06-04 19:00:14 -05:00
github-actions
23869cab0d 📝 Update release notes 2024-06-04 23:58:51 +00:00
Mieszko Bańczerowski
24e76c7a13 ✏️ Fix typo in docs/tutorial/relationship-attributes/index.md (#880) 2024-06-04 18:58:27 -05:00
github-actions
6e7e553963 📝 Update release notes 2024-06-04 23:57:10 +00:00
Anderson T
e7c62fc9d9 ✏️ Fix typo in sqlmodel/_compat.py (#950) 2024-06-04 18:56:52 -05:00
github-actions
8703539bf0 📝 Update release notes 2024-06-04 23:48:20 +00:00
Alejandra
f6ad19b1a7 ✏️ Update pip installation command in tutorial (#975) 2024-06-04 23:48:02 +00:00
Sebastián Ramírez
4590963e88 🔖 Release version 0.0.19 2024-06-03 22:26:53 -05:00
github-actions
883cbe3a8d 📝 Update release notes 2024-06-04 03:22:28 +00:00
Sebastián Ramírez
b560e9deb8 ⬆️ Upgrade Ruff and Black (#968)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-06-03 22:22:04 -05:00
github-actions
e2f646dea5 📝 Update release notes 2024-06-04 03:19:04 +00:00
dependabot[bot]
b93dd95125 ⬆ Bump tiangolo/issue-manager from 0.4.1 to 0.5.0 (#922)
Bumps [tiangolo/issue-manager](https://github.com/tiangolo/issue-manager) from 0.4.1 to 0.5.0.
- [Release notes](https://github.com/tiangolo/issue-manager/releases)
- [Commits](https://github.com/tiangolo/issue-manager/compare/0.4.1...0.5.0)

---
updated-dependencies:
- dependency-name: tiangolo/issue-manager
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-03 22:18:48 -05:00
github-actions
ceac7bc2e8 📝 Update release notes 2024-06-04 02:48:00 +00:00
Esteban Maya
1d43bd8b1e 🐛 Fix pydantic EmailStr support and max_length in several String subclasses (#966) 2024-06-03 21:47:40 -05:00
github-actions
9f3af8507e 📝 Update release notes 2024-06-04 02:35:16 +00:00
Sebastián Ramírez
d165e4b5ad ♻️ Refactor generate select template to isolate templated code to the minimum (#967)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-06-03 21:34:54 -05:00
github-actions
d5cba6e358 📝 Update release notes 2024-06-04 01:39:25 +00:00
Sebastián Ramírez
bd1641c9a2 ⬆️ Update minimum SQLAlchemy version to 2.0.14 as that one includes TryCast used internally (#964) 2024-06-03 20:39:07 -05:00
github-actions
71de44daba 📝 Update release notes 2024-06-04 01:34:41 +00:00
Sebastián Ramírez
1b275bd6a7 📌 Pin typing-extensions in tests for compatiblity with Python 3.8, dirty-equals, Pydantic (#965) 2024-06-04 01:34:21 +00:00
github-actions
866d9ecb29 📝 Update release notes 2024-06-04 00:39:40 +00:00
Esteban Maya
5bb4cffd49 🐛 Fix set varchar limit when max_length is set on Pydantic models using Pydantic v2 (#963)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2024-06-04 00:39:23 +00:00
github-actions
a319952be1 📝 Update release notes 2024-06-03 23:56:49 +00:00
Soof Golan
662bd641b8 ✏️ Fix broken link to @dataclass_transform (now PEP 681) in docs/features.md (#753)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
Co-authored-by: Patrick Arminio <patrick.arminio@gmail.com>
2024-06-03 23:56:30 +00:00
github-actions
dcf4f58e81 📝 Update release notes 2024-05-10 21:00:41 +00:00
Sebastián Ramírez
e4013acc54 👷 Update GitHub Actions to download and upload artifacts (#936) 2024-05-10 21:00:24 +00:00
github-actions
df0f834227 📝 Update release notes 2024-05-07 18:32:44 +00:00
Sebastián Ramírez
5e592c9a0d 👷 Tweak CI for test-redistribute, add needed env vars for slim (#929) 2024-05-07 18:32:16 +00:00
Sebastián Ramírez
c13b71056e 📝 Update release notes 2024-04-29 23:29:21 -07:00
Sebastián Ramírez
900e0d3371 🔖 Release version 0.0.18 2024-04-29 23:25:02 -07:00
github-actions
a280b58c10 📝 Update release notes 2024-04-30 06:22:46 +00:00
Sebastián Ramírez
9ebbf255f7 Add sqlmodel-slim setup (#916) 2024-04-30 06:22:28 +00:00
github-actions
39cbf27904 📝 Update release notes 2024-04-30 00:01:02 +00:00
Sebastián Ramírez
28d0e76370 🔧 Re-enable MkDocs Material Social plugin (#915) 2024-04-30 00:00:40 +00:00
Sebastián Ramírez
0431c5bb26 🔖 Release version 0.0.17 2024-04-29 16:44:21 -07:00
github-actions
1c4f425f17 📝 Update release notes 2024-04-29 23:35:03 +00:00
dependabot[bot]
3ecbeacb46 ⬆ Bump actions/setup-python from 4 to 5 (#733)
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2024-04-29 23:34:38 +00:00
github-actions
32353eeb07 📝 Update release notes 2024-04-29 23:26:22 +00:00
Sebastián Ramírez
7023896d7c 🔨 Update internal scripts and remove unused ones (#914) 2024-04-29 16:24:50 -07:00
github-actions
570cd9f10c 📝 Update release notes 2024-04-29 22:58:36 +00:00
Sebastián Ramírez
0b4989d0b2 🔧 Migrate from Poetry to PDM for the internal build config (#912) 2024-04-29 15:58:15 -07:00
github-actions
f67867f974 📝 Update release notes 2024-04-29 22:11:20 +00:00
Sebastián Ramírez
2454694de3 ♻️ Refactor types to properly support Pydantic 2.7 (#913) 2024-04-29 15:11:02 -07:00
github-actions
6151f23e15 📝 Update release notes 2024-04-08 23:08:14 +00:00
Esteban Maya
1eb40b1f33 📝 Update ModelRead to ModelPublic documentation and examples (#885)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2024-04-08 18:07:48 -05:00
github-actions
fa79856a4b 📝 Update release notes 2024-04-08 22:52:39 +00:00
Sebastián Ramírez
fce7ee21a2 🔧 Update MkDocs, disable cards while I can upgrade to the latest MkDocs Material, that fixes an issue with social cards (#888) 2024-04-08 17:52:21 -05:00
github-actions
c75743d9d3 📝 Update release notes 2024-04-02 01:51:04 +00:00
Esteban Maya
355f48860c 👷 Add cron to run test once a week on monday (#869) 2024-04-01 20:50:48 -05:00
github-actions
51df778420 📝 Update release notes 2024-03-26 17:35:41 +00:00
Sebastián Ramírez
bf51a11dcf ⬆️ Upgrade Ruff version and configs (#859) 2024-03-26 17:35:15 +00:00
Sebastián Ramírez
9d0b8b6a93 📝 Update release notes 2024-03-21 17:54:34 -05:00
github-actions
0e3154a553 📝 Update release notes 2024-03-21 22:49:58 +00:00
Sebastián Ramírez
9141c8a920 Add source examples for Python 3.10 and 3.9 with updated syntax (#842)
Co-authored-by: Esteban Maya Cadavid <emayacadavid9@gmail.com>
2024-03-21 17:49:38 -05:00
github-actions
4c3f242ae2 📝 Update release notes 2024-03-13 19:31:41 +00:00
Sebastián Ramírez
0a80166b81 🔥 Remove Jina QA Bot as it has been discontinued (#840) 2024-03-13 20:31:20 +01:00
Sebastián Ramírez
6b562358fc 🔖 Release version 0.0.16 2024-02-17 14:53:16 +01:00
Sebastián Ramírez
3d483921fe 📝 Update release notes 2024-02-17 14:52:43 +01:00
github-actions
2abb798a22 📝 Update release notes 2024-02-17 13:49:59 +00:00
Sebastián Ramírez
fa12c5d87b Add new method sqlmodel_update() to update models in place, including an update parameter for extra data (#804) 2024-02-17 14:49:39 +01:00
Sebastián Ramírez
7fec884864 🔖 Release version 0.0.15 2024-02-17 14:36:12 +01:00
github-actions
d8fa545955 📝 Update release notes 2024-02-17 13:35:13 +00:00
Sebastián Ramírez
1b7b3aa668 🐛 Fix class initialization compatibility with Pydantic and SQLModel, fixing errors revealed by the latest Pydantic (#807) 2024-02-17 13:34:57 +00:00
github-actions
0c7def88b5 📝 Update release notes 2024-01-09 06:52:30 +00:00
dependabot[bot]
48b97f3d8e ⬆ Bump tiangolo/issue-manager from 0.4.0 to 0.4.1 (#775)
Bumps [tiangolo/issue-manager](https://github.com/tiangolo/issue-manager) from 0.4.0 to 0.4.1.
- [Release notes](https://github.com/tiangolo/issue-manager/releases)
- [Commits](https://github.com/tiangolo/issue-manager/compare/0.4.0...0.4.1)

---
updated-dependencies:
- dependency-name: tiangolo/issue-manager
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-09 07:49:37 +01:00
github-actions
fe497adf0c 📝 Update release notes 2023-12-10 20:20:18 +00:00
Sebastián Ramírez
8419545a3a 👷 Fix GitHub Actions build docs filter paths for GitHub workflows (#738)
* 👷 Fix GitHub Actions build docs filter paths for GitHub workflows

* 🎨 Update format of expression and conftest
2023-12-10 20:19:12 +00:00
Sebastián Ramírez
b892504141 🔖 Release version 0.0.14 2023-12-04 15:51:20 +01:00
github-actions
bd24013a26 📝 Update release notes 2023-12-04 14:42:57 +00:00
Sebastián Ramírez
fa2f178b8a Add support for Pydantic v2 (while keeping support for v1 if v2 is not available), including initial work by AntonDeMeester (#722)
Co-authored-by: Mohamed Farahat <farahats9@yahoo.com>
Co-authored-by: Stefan Borer <stefan.borer@gmail.com>
Co-authored-by: Peter Landry <peter.landry@gmail.com>
Co-authored-by: Anton De Meester <antondemeester+github@gmail.com>
2023-12-04 15:42:39 +01:00
Sebastián Ramírez
5b733b348d 🔖 Release version 0.0.13 2023-12-04 13:15:10 +01:00
github-actions
6770b9fd89 📝 Update release notes 2023-12-04 12:13:24 +00:00
Sebastián Ramírez
5c0fca1d96 ✏️ Fix typo, simplify single quote/apostrophe character in "Sister Margaret's" everywhere in the docs (#721) 2023-12-04 13:13:03 +01:00
github-actions
909286cc03 📝 Update release notes 2023-12-04 12:01:06 +00:00
Sebastián Ramírez
276bcf788c 🔧 Update docs build setup, add support for sponsors, add sponsor GOVCERT.LU (#720) 2023-12-04 13:00:47 +01:00
github-actions
cc11619c67 📝 Update release notes 2023-12-04 09:49:42 +00:00
Sebastián Ramírez
41495e30c7 📝 Update docs for Decimal, use proper types (#719) 2023-12-04 09:49:23 +00:00
github-actions
50b0198423 📝 Update release notes 2023-12-04 09:47:19 +00:00
Sebastián Ramírez
853d787923 ♻️ Refactor type generation of selects re-order to prioritize models to optimize editor support (#718) 2023-12-04 09:46:59 +00:00
github-actions
e5fddb97a7 📝 Update release notes 2023-11-30 15:23:25 +00:00
Sebastián Ramírez
4ac87146b1 🔇 Do not raise deprecation warnings for execute as it's automatically used internally (#716)
* 🔇 Do not raise deprecation warnings for execute as it's automatically used internally

*  Tweak tests to not use deprecated query
2023-11-30 15:23:06 +00:00
github-actions
2ecc86275f 📝 Update release notes 2023-11-30 15:20:09 +00:00
pre-commit-ci[bot]
f42314956f ⬆ [pre-commit.ci] pre-commit autoupdate (#697)
updates:
- [github.com/astral-sh/ruff-pre-commit: v0.1.4 → v0.1.6](https://github.com/astral-sh/ruff-pre-commit/compare/v0.1.4...v0.1.6)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-11-30 16:19:47 +01:00
github-actions
98739b071c 📝 Update release notes 2023-11-29 15:52:16 +00:00
Sebastián Ramírez
d8effcbc5c 📝 Add source examples for Python 3.9 and 3.10 (#715)
* 📝 Add source examples for Python 3.9 and 3.10

*  Add tests for new source examples for Python 3.9 and 3.10, still needs pytest markers

*  Add tests for fastapi examples

*  Update tests for FastAPI app testing, for Python 3.9 and 3.10, fixing multi-app testing conflicts

*  Require Python 3.9 and 3.10 for tests

*  Update tests with missing markers
2023-11-29 16:51:55 +01:00
github-actions
cce30d7546 📝 Update release notes 2023-11-28 22:41:22 +00:00
Sebastián Ramírez
33c5e5c98d 🔧 Show line numbers in docs during local development (#714)
🔧 Show line numbers during local development
2023-11-28 23:41:03 +01:00
github-actions
2b0dfb50c8 📝 Update release notes 2023-11-28 22:12:55 +00:00
Sebastián Ramírez
799d0aa7a6 📝 Update details syntax with new pymdown extensions format (#713) 2023-11-28 23:12:33 +01:00
github-actions
be464fba69 📝 Update release notes 2023-11-28 20:50:54 +00:00
Sebastián Ramírez
a95bd3873d 🔧 Update config with new pymdown extensions (#712)
* 🔧 Update config with new pymdown extensions

* 📝 Update admonition blocks syntax

* 📝 Update syntax for tabs with new pymdown extensions
2023-11-28 21:50:33 +01:00
github-actions
71baff6015 📝 Update release notes 2023-11-27 09:58:47 +00:00
Sebastián Ramírez
f18ea03b07 🙈 Update gitignore, include all coverage files (#711) 2023-11-27 09:58:25 +00:00
github-actions
65ee2610b6 📝 Update release notes 2023-11-26 14:20:17 +00:00
Sebastián Ramírez
47bcd9df8d ⬆️ Add support for Python 3.11 and Python 3.12 (#710) 2023-11-26 15:20:01 +01:00
github-actions
781a2d6b0a 📝 Update release notes 2023-11-26 13:57:29 +00:00
Sebastián Ramírez
a974d9104f Move OpenAPI tests inline to simplify updating them with Pydantic v2 (#709) 2023-11-26 13:57:12 +00:00
Sebastián Ramírez
b1c2f822c9 🔖 Release version 0.0.12 2023-11-18 12:32:59 +01:00
Sebastián Ramírez
382b1b0cbb 📝 Update release notes 2023-11-18 12:32:24 +01:00
github-actions
6d00f6fcbd 📝 Update release notes 2023-11-18 11:30:55 +00:00
Sebastián Ramírez
8ed856d322 Upgrade SQLAlchemy to 2.0, including initial work by farahats9 (#700)
Co-authored-by: Mohamed Farahat <farahats9@yahoo.com>
Co-authored-by: Stefan Borer <stefan.borer@gmail.com>
Co-authored-by: Peter Landry <peter.landry@gmail.com>
2023-11-18 12:30:37 +01:00
github-actions
77c6fed305 📝 Update release notes 2023-11-08 16:04:47 +00:00
pre-commit-ci[bot]
c0294423f4 ⬆ [pre-commit.ci] pre-commit autoupdate (#686)
updates:
- https://github.com/charliermarsh/ruff-pre-commithttps://github.com/astral-sh/ruff-pre-commit
- [github.com/astral-sh/ruff-pre-commit: v0.1.2 → v0.1.4](https://github.com/astral-sh/ruff-pre-commit/compare/v0.1.2...v0.1.4)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-11-08 17:04:24 +01:00
github-actions
e05eae6a49 📝 Update release notes 2023-11-04 01:43:16 +00:00
Sebastián Ramírez
ed22232dee 👷 Upgrade latest-changes GitHub Action (#693) 2023-11-04 01:42:52 +00:00
Sebastián Ramírez
dacc1fa9ca 🔖 Release version 0.0.11 2023-10-29 13:56:39 +04:00
Sebastián Ramírez
0b2d015fc4 📝 Update release notes 2023-10-29 13:56:00 +04:00
github-actions
d8a20d9c21 📝 Update release notes 2023-10-29 09:21:52 +00:00
dependabot[bot]
188f7cd172 ⬆ Update coverage requirement from ^6.2 to >=6.2,<8.0 (#663)
Updates the requirements on [coverage](https://github.com/nedbat/coveragepy) to permit the latest version.
- [Release notes](https://github.com/nedbat/coveragepy/releases)
- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst)
- [Commits](https://github.com/nedbat/coveragepy/compare/6.2...7.2.7)

---
updated-dependencies:
- dependency-name: coverage
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-29 09:18:26 +00:00
github-actions
12af94655b 📝 Update release notes 2023-10-29 09:09:02 +00:00
dependabot[bot]
4bb99763b1 ⬆ Update mkdocs-material requirement from 9.1.21 to 9.2.7 (#675)
Updates the requirements on [mkdocs-material](https://github.com/squidfunk/mkdocs-material) to permit the latest version.
- [Release notes](https://github.com/squidfunk/mkdocs-material/releases)
- [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG)
- [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.1.21...9.2.7)

---
updated-dependencies:
- dependency-name: mkdocs-material
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-29 13:08:22 +04:00
github-actions
9dd11ef74f 📝 Update release notes 2023-10-29 09:00:31 +00:00
Sebastián Ramírez
fa5d14e413 ⬆️ Upgrade mypy manually (#684) 2023-10-29 08:59:56 +00:00
github-actions
df1efcfa7f 📝 Update release notes 2023-10-29 08:55:43 +00:00
dependabot[bot]
fc1d7afae9 ⬆ Update black requirement from ^22.10.0 to >=22.10,<24.0 (#664)
Updates the requirements on [black](https://github.com/psf/black) to permit the latest version.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/compare/22.10.0...23.3.0)

---
updated-dependencies:
- dependency-name: black
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-29 08:55:04 +00:00
github-actions
a2d1b4eeaf 📝 Update release notes 2023-10-29 08:52:01 +00:00
Sebastián Ramírez
94f3765fcf 👷 Update CI to build MkDocs Insiders only when the secrets are available, for Dependabot (#683) 2023-10-29 12:51:26 +04:00
github-actions
31ed654dd0 📝 Update release notes 2023-10-29 08:11:21 +00:00
Maruo.S
cbaf172c63 Add support for passing a custom SQLAlchemy type to Field() with sa_type (#505)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-10-29 12:10:39 +04:00
github-actions
c557cf6d18 📝 Update release notes 2023-10-29 07:25:17 +00:00
Matthieu LAURENT
9632980664 🎨 Update inline source examples, hide # in annotations (from MkDocs Material) (#677)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-10-29 07:24:32 +00:00
github-actions
6457775a0f 📝 Update release notes 2023-10-28 13:55:56 +00:00
Sebastián Ramírez
717594ef13 Do not allow invalid combinations of field parameters for columns and relationships, sa_column excludes sa_column_args, primary_key, nullable, etc. (#681)
* ♻️ Make sa_column exclusive, do not allow incompatible arguments, sa_column_args, primary_key, etc

*  Add tests for new errors when incorrectly using sa_column

*  Add tests for sa_column_args and sa_column_kwargs

* ♻️ Do not allow sa_relationship with sa_relationship_args or sa_relationship_kwargs

*  Add tests for relationship errors

*  Fix test for sa_column_args
2023-10-28 17:55:23 +04:00
Sebastián Ramírez
e4e1385eed 🔖 Release version 0.0.10 2023-10-26 18:34:49 +04:00
github-actions
13cc722110 📝 Update release notes 2023-10-26 14:32:59 +00:00
Sebastián Ramírez
7fdfee10a5 🔧 Adopt Ruff for formatting (#679) 2023-10-26 18:32:26 +04:00
github-actions
8d14232538 📝 Update release notes 2023-10-26 10:18:41 +00:00
Daniil Fajnberg
99f8ce3894 Add support for all Field parameters from Pydantic 1.9.0 and above, make Pydantic 1.9.0 the minimum required version (#440)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-10-26 14:18:05 +04:00
Sebastián Ramírez
d05c3ee495 🔖 Release version 0.0.9 2023-10-24 01:01:18 +04:00
Sebastián Ramírez
596718d93b 📝 Update release notes 2023-10-24 00:59:49 +04:00
github-actions
a6ce817ca5 📝 Update release notes 2023-10-23 20:46:39 +00:00
github-actions
dcc4e4c36a 📝 Update release notes 2023-10-23 20:46:26 +00:00
Simon Weiß
80fd7e03cf 📝 Clarify description of in-memory SQLite database in docs/tutorial/create-db-and-table.md (#601)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-10-23 20:46:05 +00:00
pre-commit-ci[bot]
1acb683f80 ⬆ [pre-commit.ci] pre-commit autoupdate (#672)
updates:
- [github.com/pre-commit/pre-commit-hooks: v4.4.0 → v4.5.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.4.0...v4.5.0)
- [github.com/asottile/pyupgrade: v3.7.0 → v3.15.0](https://github.com/asottile/pyupgrade/compare/v3.7.0...v3.15.0)
- https://github.com/charliermarsh/ruff-pre-commithttps://github.com/astral-sh/ruff-pre-commit
- [github.com/astral-sh/ruff-pre-commit: v0.0.275 → v0.1.1](https://github.com/astral-sh/ruff-pre-commit/compare/v0.0.275...v0.1.1)
- [github.com/psf/black: 23.3.0 → 23.10.0](https://github.com/psf/black/compare/23.3.0...23.10.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-10-24 00:45:45 +04:00
github-actions
9fd7306648 📝 Update release notes 2023-10-23 20:37:48 +00:00
Sebastián Ramírez
9d3ca01dd0 📝 Tweak wording in docs/tutorial/fastapi/multiple-models.md (#674)
Co-authored-by: Luis Benitez <lbenitez000@gmail.com>
2023-10-23 20:37:07 +00:00
github-actions
6cd086f25f 📝 Update release notes 2023-10-23 19:22:23 +00:00
PookieBuns
376603efb2 ✏️ Fix contributing instructions to run tests, update script name (#634)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-10-23 19:21:38 +00:00
github-actions
30d2b30217 📝 Update release notes 2023-10-23 17:22:58 +00:00
Abenezer Belachew
9b186c89a8 📝 Update link to docs for intro to databases (#593)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-10-23 17:22:22 +00:00
github-actions
fc3120a877 📝 Update release notes 2023-10-23 17:06:30 +00:00
dependabot[bot]
6d361e3ffb ⬆ Bump dawidd6/action-download-artifact from 2.24.2 to 2.28.0 (#660)
Bumps [dawidd6/action-download-artifact](https://github.com/dawidd6/action-download-artifact) from 2.24.2 to 2.28.0.
- [Release notes](https://github.com/dawidd6/action-download-artifact/releases)
- [Commits](https://github.com/dawidd6/action-download-artifact/compare/v2.24.2...v2.28.0)

---
updated-dependencies:
- dependency-name: dawidd6/action-download-artifact
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-23 21:05:55 +04:00
github-actions
03f295b397 📝 Update release notes 2023-10-23 16:56:29 +00:00
Jon Michaelchuck
403d44ea78 📝 Update docs, use offset in example with limit and where (#273)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-10-23 20:55:53 +04:00
github-actions
a1caaa08d7 📝 Update release notes 2023-10-23 16:29:36 +00:00
Jerry Wu
d192142eb9 📝 Fix docs for Pydantic's fields using le (lte is invalid, use le ) (#207)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-10-23 16:28:51 +00:00
github-actions
beb7a24275 📝 Update release notes 2023-10-23 15:33:44 +00:00
Sebastián Ramírez
dee70033b8 Refactor OpenAPI FastAPI tests to simplify updating them later, this moves things around without changes (#671) 2023-10-23 15:33:08 +00:00
github-actions
189059e07e 📝 Update release notes 2023-10-23 15:32:48 +00:00
dependabot[bot]
475b838c8b ⬆ Bump actions/checkout from 3 to 4 (#670)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-23 19:32:09 +04:00
github-actions
d6e4f9b9e3 📝 Update release notes 2023-10-23 15:16:53 +00:00
Michael Oliver
1062e1b485 🔧 Update mypy config, use strict = true instead of manual configs (#428)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-10-23 19:16:17 +04:00
github-actions
8e55ea5125 📝 Update release notes 2023-10-23 14:58:52 +00:00
Arseny Boykov
9732c5ac60 🐛 Fix AsyncSession type annotations for exec() (#58)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-10-23 18:58:16 +04:00
github-actions
b8996f0e62 📝 Update release notes 2023-10-23 13:59:53 +00:00
Daniil Fajnberg
9809b5bc83 🐛 Fix allowing using a ForeignKey directly, remove repeated column construction from SQLModelMetaclass.__init__ and upgrade minimum SQLAlchemy to >=1.4.36 (#443)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-10-23 17:59:06 +04:00
github-actions
c213f5daf4 📝 Update release notes 2023-10-23 11:22:53 +00:00
Sandro Tosi
d1cf613461 ⬆️ Upgrade support for SQLAlchemy 1.4.49, update tests (#519)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2023-10-23 15:22:10 +04:00
github-actions
d281a0fa9f 📝 Update release notes 2023-10-23 09:40:31 +00:00
Daniil Fajnberg
9511c4677d ⬆ Raise SQLAlchemy version requirement to at least 1.4.29 (related to #434) (#439)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-10-23 13:39:55 +04:00
github-actions
c25a8cb89b 📝 Update release notes 2023-10-23 09:23:24 +00:00
Sebastián Ramírez
d3261cab59 🐛 Fix enum type checks ordering in get_sqlalchemy_type (#669)
Co-authored-by: Pierre Cheynier <p.cheynier@criteo.com>
2023-10-23 13:22:44 +04:00
github-actions
40c1af9202 📝 Update release notes 2023-10-23 08:15:40 +00:00
Sebastián Ramírez
b83e848699 ⬆️ Upgrade MkDocs Material (#668) 2023-10-23 08:15:05 +00:00
github-actions
2676cf2b06 📝 Update release notes 2023-10-23 08:12:08 +00:00
Sebastián Ramírez
56f43904c1 🎨 Update docs format and references with pre-commit and Ruff (#667) 2023-10-23 08:11:36 +00:00
github-actions
7c5894ee75 📝 Update release notes 2023-10-23 07:47:04 +00:00
Sebastián Ramírez
27a81b2112 🎨 Run pre-commit on all files and autoformat (#666) 2023-10-23 11:46:31 +04:00
github-actions
7f72c60ae4 📝 Update release notes 2023-10-23 07:35:25 +00:00
Sebastián Ramírez
065fcdc828 👷 Move to Ruff and add pre-commit (#661)
* 👷 Add pre-commit

* 🔧 Add pyproject.toml config for Ruff

*  Replace isort, flake8, autoflake with Ruff

* 🔨 Update lint and format scripts

* 🎨 Format with Ruff

* 🔧 Update Poetry config
2023-10-23 07:34:50 +00:00
github-actions
9ba3039106 📝 Update release notes 2023-10-23 06:43:04 +00:00
David Danier
840fd08ab2 Raise a more clear error when a type is not valid (#425)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-10-23 10:42:30 +04:00
github-actions
a8a792e3c0 📝 Update release notes 2023-10-22 12:55:08 +00:00
Matvey Fedoseev
8970833b14 📝 Update outdated link in docs/db-to-code.md (#649)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-10-22 12:54:36 +00:00
github-actions
5231c8b6bb 📝 Update release notes 2023-10-22 12:51:20 +00:00
Kian-Meng Ang
1568bad01e ✏️ Fix typos found with codespell (#520)
Found via `codespell -S *.svg,*.css,*.js,*.drawio -L pullrequest,sesion`
2023-10-22 16:50:44 +04:00
github-actions
357417e6d5 📝 Update release notes 2023-10-22 12:47:27 +00:00
Dipendra Raj Panta
893d64fd31 📝 Fix typos (duplication) in main page (#631)
* Update README.md

* Update index.md
2023-10-22 16:46:52 +04:00
github-actions
2799303f6c 📝 Update release notes 2023-10-22 12:27:13 +00:00
Ben Rapaport
a2e2942aad 📝 Update release notes, add second author to PR (#429)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-10-22 12:26:37 +00:00
github-actions
b7d6a0a3c3 📝 Update release notes 2023-10-22 12:04:24 +00:00
Jorge Alvarado
bdcf11bca6 📝 Update instructions about how to make a foreign key required in docs/tutorial/relationship-attributes/define-relationships-attributes.md (#474)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-10-22 16:03:51 +04:00
github-actions
d939b7c45c 📝 Update release notes 2023-10-22 12:02:24 +00:00
byrman
d5219aa3c5 🐛 Fix SQLAlchemy version 1.4.36 breaks SQLModel relationships (#315) (#461)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-10-22 16:01:51 +04:00
github-actions
aa7169b93b 📝 Update release notes 2023-10-22 10:03:26 +00:00
Sugato Ray
89c356cb77 🛠️ Add CITATION.cff file for academic citations (#13)
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2023-10-22 14:02:53 +04:00
github-actions
088164ef2a 📝 Update release notes 2023-08-01 09:19:41 +00:00
Sebastián Ramírez
1e2fd10047 👷 Update docs deployments to Cloudflare (#630) 2023-08-01 11:18:53 +02:00
github-actions
30a9c23a34 📝 Update release notes 2023-07-31 19:53:00 +00:00
github-actions
da29f7940d 📝 Update release notes 2023-07-31 19:52:51 +00:00
github-actions
40007c80da 📝 Update release notes 2023-07-31 19:48:54 +00:00
Sebastián Ramírez
e246ae8864 👷 Update CI debug mode with Tmate (#629) 2023-07-31 19:48:21 +00:00
github-actions
aaa54492c1 📝 Update release notes 2023-07-31 19:39:15 +00:00
Sebastián Ramírez
73fa81af74 👷 Update latest changes token (#616) 2023-07-31 19:38:43 +00:00
Sebastián Ramírez
a21d5c85a3 👷‍♂️ Upgrade CI for docs (#628) 2023-07-31 17:15:43 +02:00
Sebastián Ramírez
02bd7ebffd 🗑️ Deprecate Python 3.6 and upgrade Poetry and Poetry Version Plugin (#627) 2023-07-29 12:32:47 +02:00
github-actions
43a689d369 📝 Update release notes 2023-02-21 11:02:54 +00:00
Sebastián Ramírez
810236c26c ⬆️ Upgrade analytics (#558) 2023-02-21 11:02:18 +00:00
github-actions
33e00c3ab3 📝 Update release notes 2023-02-03 17:53:01 +00:00
Sebastián Ramírez
5c9a3b3b21 📝 Update help SQLModel docs (#548) 2023-02-03 18:52:25 +01:00
github-actions
dc44d2e2c4 📝 Update release notes 2023-01-31 14:14:55 +00:00
Sebastián Ramírez
1c294ddeb6 🔧 Update new issue chooser to point to GitHub Discussions (#546) 2023-01-31 15:14:16 +01:00
github-actions
c47a54df91 📝 Update release notes 2023-01-30 10:50:32 +00:00
Sebastián Ramírez
624a2142bf 🔧 Add template for GitHub Discussion questions and update issues template (#544) 2023-01-30 11:49:56 +01:00
github-actions
7b3148c0b4 📝 Update release notes 2022-12-16 18:46:26 +00:00
Sebastián Ramírez
cf36b2d9ba 👷 Refactor CI artifact upload/download for docs previews (#514) 2022-12-16 18:45:51 +00:00
github-actions
5fa9062ed9 📝 Update release notes 2022-11-12 06:44:52 +00:00
Colin Marquardt
267cd42fb6 ✏️ Fix typo in internal function name get_sqlachemy_type() (#496)
Corrected name is get_sqlalchemy_type().
2022-11-12 07:44:19 +01:00
github-actions
497270dc55 📝 Update release notes 2022-11-12 06:38:16 +00:00
dependabot[bot]
aa87ff37ea ⬆ Bump actions/cache from 2 to 3 (#497)
Bumps [actions/cache](https://github.com/actions/cache) from 2 to 3.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v2...v3)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-12 07:37:34 +01:00
github-actions
0fe1e6d5a3 📝 Update release notes 2022-11-11 17:30:42 +00:00
David Brochart
54b5db9842 ✏️ Fix typo in docs (#446)
* Fix typo

* Again

Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2022-11-11 17:30:09 +00:00
github-actions
c31bac638c 📝 Update release notes 2022-11-10 06:43:44 +00:00
dependabot[bot]
203db14e9b ⬆ Bump dawidd6/action-download-artifact from 2.24.0 to 2.24.2 (#493)
Bumps [dawidd6/action-download-artifact](https://github.com/dawidd6/action-download-artifact) from 2.24.0 to 2.24.2.
- [Release notes](https://github.com/dawidd6/action-download-artifact/releases)
- [Commits](https://github.com/dawidd6/action-download-artifact/compare/v2.24.0...v2.24.2)

---
updated-dependencies:
- dependency-name: dawidd6/action-download-artifact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-10 07:43:06 +01:00
github-actions
06bb369bcb 📝 Update release notes 2022-11-08 15:04:40 +00:00
Santhosh Solomon
209791734c ✏️ Fix typo in docs/tutorial/create-db-and-table.md (#477)
typo fixed in docs/tutorial/create-db-and-table.md

Co-authored-by: Santhosh Solomon <santhoshsolomon@Santhoshs-MacBook-Air.local>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2022-11-08 15:04:06 +00:00
github-actions
fbc6f3b536 📝 Update release notes 2022-11-07 14:21:59 +00:00
Michael Cuffaro
22faa192b9 ✏️ Fix small typos in docs (#481)
Fix small typos

Fixing small typos here and there while reading the document.

Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2022-11-07 14:21:23 +00:00
github-actions
3a9244c69f 📝 Update release notes 2022-11-05 01:04:44 +00:00
Sebastián Ramírez
8070b142b8 🔧 Update Smokeshow coverage threshold (#487) 2022-11-05 02:04:13 +01:00
github-actions
f0aa199611 📝 Update release notes 2022-11-04 22:02:09 +00:00
Sebastián Ramírez
5956940908 👷 Move from Codecov to Smokeshow (#486) 2022-11-04 22:01:37 +00:00
github-actions
ea79c47c0e 📝 Update release notes 2022-11-04 21:44:25 +00:00
dependabot[bot]
7e26dac198 ⬆ Bump actions/setup-python from 2 to 4 (#411)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2022-11-04 22:43:45 +01:00
github-actions
bb32178bda 📝 Update release notes 2022-11-04 21:32:51 +00:00
dependabot[bot]
5ca9375f26 ⬆ Update black requirement from ^21.5-beta.1 to ^22.10.0 (#460)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2022-11-04 22:32:13 +01:00
github-actions
e7d8b69b53 📝 Update release notes 2022-11-04 21:18:47 +00:00
Sebastián Ramírez
c6ad5b8109 Add extra dev dependencies for MkDocs Material (#485) 2022-11-04 22:16:59 +01:00
github-actions
8003c73877 📝 Update release notes 2022-11-04 21:14:56 +00:00
github-actions
b532a4c304 📝 Update release notes 2022-11-04 21:14:34 +00:00
dependabot[bot]
29d9721d1a ⬆ Update mypy requirement from 0.930 to 0.971 (#380)
Update mypy requirement from 0.930 to 0.971

Updates the requirements on [mypy](https://github.com/python/mypy) to permit the latest version.
- [Release notes](https://github.com/python/mypy/releases)
- [Commits](https://github.com/python/mypy/compare/v0.930...v0.971)

---
updated-dependencies:
- dependency-name: mypy
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-04 22:14:11 +01:00
dependabot[bot]
f0f6f93e28 ⬆ Update coverage requirement from ^5.5 to ^6.2 (#171)
Update coverage requirement from ^5.5 to ^6.2

Updates the requirements on [coverage](https://github.com/nedbat/coveragepy) to permit the latest version.
- [Release notes](https://github.com/nedbat/coveragepy/releases)
- [Changelog](https://github.com/nedbat/coveragepy/blob/master/CHANGES.rst)
- [Commits](https://github.com/nedbat/coveragepy/compare/coverage-5.5...6.2)

---
updated-dependencies:
- dependency-name: coverage
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2022-11-04 22:13:58 +01:00
github-actions
91d8e38208 📝 Update release notes 2022-11-04 21:03:39 +00:00
github-actions
ccdab8fb24 📝 Update release notes 2022-11-04 21:02:59 +00:00
dependabot[bot]
592c877cfc ⬆ Bump codecov/codecov-action from 2 to 3 (#415)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-04 22:00:58 +01:00
dependabot[bot]
fee7ecf619 ⬆ Bump actions/upload-artifact from 2 to 3 (#412)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-04 22:00:21 +01:00
github-actions
e8f61fb9d0 📝 Update release notes 2022-11-04 21:00:05 +00:00
dependabot[bot]
781174e6e9 ⬆ Update flake8 requirement from ^3.9.2 to ^5.0.4 (#396)
Updates the requirements on [flake8](https://github.com/pycqa/flake8) to permit the latest version.
- [Release notes](https://github.com/pycqa/flake8/releases)
- [Commits](https://github.com/pycqa/flake8/compare/3.9.2...5.0.4)

---
updated-dependencies:
- dependency-name: flake8
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Sebastián Ramírez <tiangolo@gmail.com>
2022-11-04 21:59:26 +01:00
github-actions
ba76745f43 📝 Update release notes 2022-11-04 20:45:07 +00:00
github-actions
5615a80fc5 📝 Update release notes 2022-11-04 20:44:06 +00:00
dependabot[bot]
375e83d068 ⬆ Update pytest requirement from ^6.2.4 to ^7.0.1 (#242)
Updates the requirements on [pytest](https://github.com/pytest-dev/pytest) to permit the latest version.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/6.2.4...7.0.1)

---
updated-dependencies:
- dependency-name: pytest
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-04 21:44:02 +01:00
github-actions
666a9a3557 📝 Update release notes 2022-11-04 20:43:32 +00:00
dependabot[bot]
1dde3e0044 ⬆ Bump actions/checkout from 2 to 3.1.0 (#458)
Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3.1.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v2...v3.1.0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-04 21:43:25 +01:00
dependabot[bot]
8b87bf8b40 ⬆ Bump dawidd6/action-download-artifact from 2.9.0 to 2.24.0 (#470)
Bumps [dawidd6/action-download-artifact](https://github.com/dawidd6/action-download-artifact) from 2.9.0 to 2.24.0.
- [Release notes](https://github.com/dawidd6/action-download-artifact/releases)
- [Commits](https://github.com/dawidd6/action-download-artifact/compare/v2.9.0...v2.24.0)

---
updated-dependencies:
- dependency-name: dawidd6/action-download-artifact
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-04 21:43:01 +01:00
github-actions
94715b6fa8 📝 Update release notes 2022-11-04 20:05:07 +00:00
Sebastián Ramírez
27a16744f2 👷 Update Dependabot config (#484) 2022-11-04 21:04:31 +01:00
538 changed files with 44299 additions and 6176 deletions

View File

@@ -1,5 +0,0 @@
[flake8]
max-line-length = 88
select = C,E,F,W,B,B9
ignore = E203, E501, W503
exclude = __init__.py

View File

@@ -1,5 +1,3 @@
name: Question or Problem
description: Ask a question or ask about a problem
labels: [question]
body:
- type: markdown
@@ -8,29 +6,29 @@ body:
Thanks for your interest in SQLModel! 🚀
Please follow these instructions, fill every question, and do every step. 🙏
I'm asking this because answering questions and solving problems in GitHub issues is what consumes most of the time.
I end up not being able to add new features, fix bugs, review pull requests, etc. as fast as I wish because I have to spend too much time handling issues.
I'm asking this because answering questions and solving problems in GitHub is what consumes most of the time.
I end up not being able to add new features, fix bugs, review pull requests, etc. as fast as I wish because I have to spend too much time handling questions.
All that, on top of all the incredible help provided by a bunch of community members that give a lot of their time to come here and help others.
If more SQLModel users came to help others like them just a little bit more, it would be much less effort for them (and you and me 😅).
By asking questions in a structured way (following this) it will be much easier to help you.
And there's a high chance that you will find the solution along the way and you won't even have to submit it and wait for an answer. 😎
As there are too many issues with questions, I'll have to close the incomplete ones. That will allow me (and others) to focus on helping people like you that follow the whole process and help us help you. 🤓
As there are too many questions, I'll have to discard and close the incomplete ones. That will allow me (and others) to focus on helping people like you that follow the whole process and help us help you. 🤓
- type: checkboxes
id: checks
attributes:
label: First Check
description: Please confirm and check all the following options.
options:
- label: I added a very descriptive title to this issue.
- label: I added a very descriptive title here.
required: true
- label: I used the GitHub search to find a similar issue and didn't find it.
- label: I used the GitHub search to find a similar question and didn't find it.
required: true
- label: I searched the SQLModel documentation, with the integrated search.
required: true
@@ -48,10 +46,10 @@ body:
label: Commit to Help
description: |
After submitting this, I commit to one of:
* Read open issues with questions until I find 2 issues where I can help someone and add a comment to help there.
* I already hit the "watch" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.
* Implement a Pull Request for a confirmed bug.
* Review one Pull Request by downloading the code and following all the review process](https://sqlmodel.tiangolo.com/help/#review-pull-requests).
options:
- label: I commit to help with one of those options 👆

View File

@@ -2,3 +2,12 @@ blank_issues_enabled: false
contact_links:
- name: Security Contact
about: Please report security vulnerabilities to security@tiangolo.com
- name: Question or Problem
about: Ask a question or ask about a problem in GitHub Discussions.
url: https://github.com/tiangolo/sqlmodel/discussions/categories/questions
- name: Feature Request
about: To suggest an idea or ask about a feature, please start with a question saying what you would like to achieve. There might be a way to do it already.
url: https://github.com/tiangolo/sqlmodel/discussions/categories/questions
- name: Show and tell
about: Show what you built with SQLModel or to be used with SQLModel.
url: https://github.com/tiangolo/sqlmodel/discussions/categories/show-and-tell

View File

@@ -1,214 +0,0 @@
name: Feature Request
description: Suggest an idea or ask for a feature that you would like to have in SQLModel
labels: [enhancement]
body:
- type: markdown
attributes:
value: |
Thanks for your interest in SQLModel! 🚀
Please follow these instructions, fill every question, and do every step. 🙏
I'm asking this because answering questions and solving problems in GitHub issues is what consumes most of the time.
I end up not being able to add new features, fix bugs, review pull requests, etc. as fast as I wish because I have to spend too much time handling issues.
All that, on top of all the incredible help provided by a bunch of community members that give a lot of their time to come here and help others.
If more SQLModel users came to help others like them just a little bit more, it would be much less effort for them (and you and me 😅).
By asking questions in a structured way (following this) it will be much easier to help you.
And there's a high chance that you will find the solution along the way and you won't even have to submit it and wait for an answer. 😎
As there are too many issues with questions, I'll have to close the incomplete ones. That will allow me (and others) to focus on helping people like you that follow the whole process and help us help you. 🤓
- type: checkboxes
id: checks
attributes:
label: First Check
description: Please confirm and check all the following options.
options:
- label: I added a very descriptive title to this issue.
required: true
- label: I used the GitHub search to find a similar issue and didn't find it.
required: true
- label: I searched the SQLModel documentation, with the integrated search.
required: true
- label: I already searched in Google "How to X in SQLModel" and didn't find any information.
required: true
- label: I already read and followed all the tutorial in the docs and didn't find an answer.
required: true
- label: I already checked if it is not related to SQLModel but to [Pydantic](https://github.com/samuelcolvin/pydantic).
required: true
- label: I already checked if it is not related to SQLModel but to [SQLAlchemy](https://github.com/sqlalchemy/sqlalchemy).
required: true
- type: checkboxes
id: help
attributes:
label: Commit to Help
description: |
After submitting this, I commit to one of:
* Read open issues with questions until I find 2 issues where I can help someone and add a comment to help there.
* I already hit the "watch" button in this repository to receive notifications and I commit to help at least 2 people that ask questions in the future.
* Implement a Pull Request for a confirmed bug.
options:
- label: I commit to help with one of those options 👆
required: true
- type: textarea
id: example
attributes:
label: Example Code
description: |
Please add a self-contained, [minimal, reproducible, example](https://stackoverflow.com/help/minimal-reproducible-example) with your use case.
If I (or someone) can copy it, run it, and see it right away, there's a much higher chance I (or someone) will be able to help you.
placeholder: |
from typing import Optional
from sqlmodel import Field, Session, SQLModel, create_engine
class Hero(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str
secret_name: str
age: Optional[int] = None
hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
engine = create_engine("sqlite:///database.db")
SQLModel.metadata.create_all(engine)
with Session(engine) as session:
session.add(hero_1)
session.commit()
session.refresh(hero_1)
print(hero_1)
render: python
validations:
required: true
- type: textarea
id: description
attributes:
label: Description
description: |
What is your feature request?
Write a short description telling me what you are trying to solve and what you are currently doing.
placeholder: |
* Create a Hero model.
* Create a Hero instance.
* Save it to a SQLite database.
* I would like it to also automatically send me an email with all the SQL code executed.
validations:
required: true
- type: textarea
id: wanted-solution
attributes:
label: Wanted Solution
description: |
Tell me what's the solution you would like.
placeholder: |
I would like it to have a `send_email` configuration that defaults to `False`, and can be set to `True` to send me an email.
validations:
required: true
- type: textarea
id: wanted-code
attributes:
label: Wanted Code
description: Show me an example of how you would want the code to look like.
placeholder: |
from typing import Optional
from sqlmodel import Field, Session, SQLModel, create_engine
class Hero(SQLModel, table=True, send_email=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str
secret_name: str
age: Optional[int] = None
hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
engine = create_engine("sqlite:///database.db")
SQLModel.metadata.create_all(engine)
with Session(engine) as session:
session.add(hero_1)
session.commit()
session.refresh(hero_1)
print(hero_1)
render: python
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives
description: |
Tell me about alternatives you've considered.
placeholder: |
To hire someone to look at the logs, write the SQL in paper, and then send me a letter by post with it.
- type: dropdown
id: os
attributes:
label: Operating System
description: What operating system are you on?
multiple: true
options:
- Linux
- Windows
- macOS
- Other
validations:
required: true
- type: textarea
id: os-details
attributes:
label: Operating System Details
description: You can add more details about your operating system here, in particular if you chose "Other".
- type: input
id: sqlmodel-version
attributes:
label: SQLModel Version
description: |
What SQLModel version are you using?
You can find the SQLModel version with:
```bash
python -c "import sqlmodel; print(sqlmodel.__version__)"
```
validations:
required: true
- type: input
id: python-version
attributes:
label: Python Version
description: |
What Python version are you using?
You can find the Python version with:
```bash
python --version
```
validations:
required: true
- type: textarea
id: context
attributes:
label: Additional Context
description: Add any additional context information or screenshots you think are useful.

22
.github/ISSUE_TEMPLATE/privileged.yml vendored Normal file
View File

@@ -0,0 +1,22 @@
name: Privileged
description: You are @tiangolo or he asked you directly to create an issue here. If not, check the other options. 👇
body:
- type: markdown
attributes:
value: |
Thanks for your interest in SQLModel! 🚀
If you are not @tiangolo or he didn't ask you directly to create an issue here, please start the conversation in a [Question in GitHub Discussions](https://github.com/tiangolo/sqlmodel/discussions/categories/questions) instead.
- type: checkboxes
id: privileged
attributes:
label: Privileged issue
description: Confirm that you are allowed to create an issue here.
options:
- label: I'm @tiangolo or he asked me directly to create an issue here.
required: true
- type: textarea
id: content
attributes:
label: Issue Content
description: Add the content of the issue here.

View File

@@ -48,9 +48,7 @@ if __name__ == "__main__":
use_pr = pr
break
if not use_pr:
logging.error(
f"No PR found for hash: {event.workflow_run.head_commit.id}"
)
logging.error(f"No PR found for hash: {event.workflow_run.head_commit.id}")
sys.exit(0)
github_headers = {
"Authorization": f"token {settings.input_token.get_secret_value()}"

View File

@@ -1,7 +0,0 @@
FROM python:3.7
RUN pip install httpx PyGithub "pydantic==1.5.1"
COPY ./app /app
CMD ["python", "/app/main.py"]

View File

@@ -1,10 +0,0 @@
name: Watch docs previews in PRs
description: Check PRs and trigger new docs deploys
author: "Sebastián Ramírez <tiangolo@gmail.com>"
inputs:
token:
description: 'Token for the repo. Can be passed in using {{ secrets.GITHUB_TOKEN }}'
required: true
runs:
using: docker
image: Dockerfile

View File

@@ -1,102 +0,0 @@
import logging
from datetime import datetime
from pathlib import Path
from typing import List, Optional
import httpx
from github import Github
from github.NamedUser import NamedUser
from pydantic import BaseModel, BaseSettings, SecretStr
github_api = "https://api.github.com"
netlify_api = "https://api.netlify.com"
main_branch = "main"
class Settings(BaseSettings):
input_token: SecretStr
github_repository: str
github_event_path: Path
github_event_name: Optional[str] = None
class Artifact(BaseModel):
id: int
node_id: str
name: str
size_in_bytes: int
url: str
archive_download_url: str
expired: bool
created_at: datetime
updated_at: datetime
class ArtifactResponse(BaseModel):
total_count: int
artifacts: List[Artifact]
def get_message(commit: str) -> str:
return f"Docs preview for commit {commit} at"
if __name__ == "__main__":
logging.basicConfig(level=logging.INFO)
settings = Settings()
logging.info(f"Using config: {settings.json()}")
g = Github(settings.input_token.get_secret_value())
repo = g.get_repo(settings.github_repository)
owner: NamedUser = repo.owner
headers = {"Authorization": f"token {settings.input_token.get_secret_value()}"}
prs = list(repo.get_pulls(state="open"))
response = httpx.get(
f"{github_api}/repos/{settings.github_repository}/actions/artifacts",
headers=headers,
)
data = response.json()
artifacts_response = ArtifactResponse.parse_obj(data)
for pr in prs:
logging.info("-----")
logging.info(f"Processing PR #{pr.number}: {pr.title}")
pr_comments = list(pr.get_issue_comments())
pr_commits = list(pr.get_commits())
last_commit = pr_commits[0]
for pr_commit in pr_commits:
if pr_commit.commit.author.date > last_commit.commit.author.date:
last_commit = pr_commit
commit = last_commit.commit.sha
logging.info(f"Last commit: {commit}")
message = get_message(commit)
notified = False
for pr_comment in pr_comments:
if message in pr_comment.body:
notified = True
logging.info(f"Docs preview was notified: {notified}")
if not notified:
artifact_name = f"docs-zip-{commit}"
use_artifact: Optional[Artifact] = None
for artifact in artifacts_response.artifacts:
if artifact.name == artifact_name:
use_artifact = artifact
break
if not use_artifact:
logging.info("Artifact not available")
else:
logging.info(f"Existing artifact: {use_artifact.name}")
response = httpx.post(
f"{github_api}/repos/{settings.github_repository}/actions/workflows/preview-docs.yml/dispatches",
headers=headers,
json={
"ref": main_branch,
"inputs": {
"pr": f"{pr.number}",
"name": artifact_name,
"commit": commit,
},
},
)
logging.info(
f"Trigger sent, response status: {response.status_code} - content: {response.content}"
)
logging.info("Finished")

View File

@@ -5,8 +5,12 @@ updates:
directory: "/"
schedule:
interval: "daily"
commit-message:
prefix:
# Python
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "daily"
commit-message:
prefix:

View File

@@ -4,78 +4,87 @@ on:
branches:
- main
pull_request:
types: [opened, synchronize]
workflow_dispatch:
inputs:
debug_enabled:
description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
required: false
default: false
types:
- opened
- synchronize
jobs:
changes:
runs-on: ubuntu-latest
# Required permissions
permissions:
pull-requests: read
# Set job outputs to values from filter step
outputs:
docs: ${{ steps.filter.outputs.docs }}
steps:
- uses: actions/checkout@v4
# For pull requests it's not necessary to checkout the code but for the main branch it is
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
docs:
- README.md
- docs/**
- docs_src/**
- requirements-docs.txt
- pyproject.toml
- mkdocs.yml
- mkdocs.insiders.yml
- .github/workflows/build-docs.yml
- .github/workflows/deploy-docs.yml
build-docs:
runs-on: ubuntu-20.04
needs:
- changes
if: ${{ needs.changes.outputs.docs == 'true' }}
runs-on: ubuntu-latest
steps:
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: "3.7"
# Allow debugging with tmate
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled }}
with:
limit-access-to-actor: true
- uses: actions/cache@v2
python-version: "3.11"
- uses: actions/cache@v4
id: cache
with:
path: ${{ env.pythonLocation }}
key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-root-docs
- name: Install poetry
key: ${{ runner.os }}-python-docs-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml', 'requirements-docs.txt') }}-v01
- name: Install docs extras
if: steps.cache.outputs.cache-hit != 'true'
# TODO: remove python -m pip install --force git+https://github.com/python-poetry/poetry-core.git@ad33bc2
# once there's a release of Poetry 1.2.x including poetry-core > 1.1.0a6
# Ref: https://github.com/python-poetry/poetry-core/pull/188
run: |
python -m pip install --upgrade pip
python -m pip install --force git+https://github.com/python-poetry/poetry-core.git@ad33bc2
python -m pip install "poetry==1.2.0a2"
python -m poetry plugin add poetry-version-plugin
- name: Configure poetry
run: python -m poetry config virtualenvs.create false
- name: Install Dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: python -m poetry install
run: pip install -r requirements-docs.txt
- name: Install Material for MkDocs Insiders
if: github.event.pull_request.head.repo.fork == false && steps.cache.outputs.cache-hit != 'true'
run: python -m poetry run pip install git+https://${{ secrets.ACTIONS_TOKEN }}@github.com/squidfunk/mkdocs-material-insiders.git
- uses: actions/cache@v2
if: ( github.event_name != 'pull_request' || github.secret_source == 'Actions' ) && steps.cache.outputs.cache-hit != 'true'
run: |
pip install git+https://${{ secrets.SQLMODEL_MKDOCS_MATERIAL_INSIDERS }}@github.com/squidfunk/mkdocs-material-insiders.git
pip install git+https://${{ secrets.SQLMODEL_MKDOCS_MATERIAL_INSIDERS }}@github.com/pawamoy-insiders/griffe-typing-deprecated.git
pip install git+https://${{ secrets.SQLMODEL_MKDOCS_MATERIAL_INSIDERS }}@github.com/pawamoy-insiders/mkdocstrings-python.git
- uses: actions/cache@v4
with:
key: mkdocs-cards-${{ github.ref }}
path: .cache
- name: Verify README
run: python ./scripts/docs.py verify-readme
- name: Build Docs
if: github.event.pull_request.head.repo.fork == true
run: python -m poetry run mkdocs build
- name: Build Docs with Insiders
if: github.event.pull_request.head.repo.fork == false
run: python -m poetry run mkdocs build --config-file mkdocs.insiders.yml
- name: Zip docs
run: python -m poetry run bash ./scripts/zip-docs.sh
- uses: actions/upload-artifact@v2
run: python ./scripts/docs.py build
- uses: actions/upload-artifact@v4
with:
name: docs-zip
path: ./docs.zip
- name: Deploy to Netlify
uses: nwtgck/actions-netlify@v1.1.5
name: docs-site
path: ./site/**
# https://github.com/marketplace/actions/alls-green#why
docs-all-green: # This job does nothing and is only used for the branch protection
if: always()
needs:
- build-docs
runs-on: ubuntu-latest
steps:
- name: Decide whether the needed jobs succeeded or failed
uses: re-actors/alls-green@release/v1
with:
publish-dir: './site'
production-branch: main
github-token: ${{ secrets.GITHUB_TOKEN }}
enable-commit-comment: false
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
jobs: ${{ toJSON(needs) }}
allowed-skips: build-docs

46
.github/workflows/deploy-docs.yml vendored Normal file
View File

@@ -0,0 +1,46 @@
name: Deploy Docs
on:
workflow_run:
workflows:
- Build Docs
types:
- completed
jobs:
deploy-docs:
runs-on: ubuntu-latest
steps:
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v4
- name: Clean site
run: |
rm -rf ./site
mkdir ./site
- uses: actions/download-artifact@v4
with:
path: ./site/
pattern: docs-site
merge-multiple: true
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}
- name: Deploy to Cloudflare Pages
# hashFiles returns an empty string if there are no files
if: hashFiles('./site/*')
id: deploy
uses: cloudflare/pages-action@v1
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
projectName: sqlmodel
directory: './site'
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
branch: ${{ ( github.event.workflow_run.head_repository.full_name == github.repository && github.event.workflow_run.head_branch == 'main' && 'main' ) || ( github.event.workflow_run.head_sha ) }}
- name: Comment Deploy
if: steps.deploy.outputs.url != ''
uses: ./.github/actions/comment-docs-preview-in-pr
with:
token: ${{ secrets.GITHUB_TOKEN }}
deploy_url: "${{ steps.deploy.outputs.url }}"

View File

@@ -18,7 +18,7 @@ jobs:
issue-manager:
runs-on: ubuntu-latest
steps:
- uses: tiangolo/issue-manager@0.4.0
- uses: tiangolo/issue-manager@0.5.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
config: >

View File

@@ -12,27 +12,30 @@ on:
description: PR number
required: true
debug_enabled:
description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
required: false
default: false
default: 'false'
jobs:
latest-changes:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
with:
# To allow latest-changes to commit to the main branch
token: ${{ secrets.ACTIONS_TOKEN }}
token: ${{ secrets.SQLMODEL_LATEST_CHANGES }}
# Allow debugging with tmate
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled }}
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled == 'true' }}
with:
limit-access-to-actor: true
- uses: docker://tiangolo/latest-changes:0.0.3
- uses: docker://tiangolo/latest-changes:0.2.0
# - uses: tiangolo/latest-changes@main
with:
token: ${{ secrets.GITHUB_TOKEN }}
latest_changes_file: docs/release-notes.md
latest_changes_header: '## Latest Changes\n\n'
latest_changes_header: '## Latest Changes'
end_regex: '^## '
debug_logs: true
label_header_prefix: '### '

View File

@@ -1,41 +0,0 @@
name: Preview Docs
on:
workflow_run:
workflows:
- Build Docs
types:
- completed
jobs:
preview-docs:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Download Artifact Docs
uses: dawidd6/action-download-artifact@v2.9.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
workflow: build-docs.yml
run_id: ${{ github.event.workflow_run.id }}
name: docs-zip
- name: Unzip docs
run: |
rm -rf ./site
unzip docs.zip
rm -f docs.zip
- name: Deploy to Netlify
id: netlify
uses: nwtgck/actions-netlify@v1.1.5
with:
publish-dir: './site'
production-deploy: false
github-token: ${{ secrets.GITHUB_TOKEN }}
enable-commit-comment: false
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
- name: Comment Deploy
uses: ./.github/actions/comment-docs-preview-in-pr
with:
token: ${{ secrets.GITHUB_TOKEN }}
deploy_url: "${{ steps.netlify.outputs.deploy-url }}"

View File

@@ -7,48 +7,31 @@ on:
workflow_dispatch:
inputs:
debug_enabled:
description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
required: false
default: false
default: 'false'
jobs:
publish:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
strategy:
matrix:
package:
- sqlmodel
- sqlmodel-slim
permissions:
id-token: write
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: "3.7"
# Allow debugging with tmate
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled }}
with:
limit-access-to-actor: true
- uses: actions/cache@v2
id: cache
with:
path: ${{ env.pythonLocation }}
key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-root
- name: Install poetry
if: steps.cache.outputs.cache-hit != 'true'
# TODO: remove python -m pip install --force git+https://github.com/python-poetry/poetry-core.git@ad33bc2
# once there's a release of Poetry 1.2.x including poetry-core > 1.1.0a6
# Ref: https://github.com/python-poetry/poetry-core/pull/188
run: |
python -m pip install --upgrade pip
python -m pip install --force git+https://github.com/python-poetry/poetry-core.git@ad33bc2
python -m pip install "poetry==1.2.0a2"
python -m poetry plugin add poetry-version-plugin
- name: Configure poetry
run: python -m poetry config virtualenvs.create false
- name: Install Dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: python -m poetry install
- name: Publish
python-version: "3.11"
- name: Install build dependencies
run: pip install build
- name: Build distribution
env:
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
run: |
python -m poetry config pypi-token.pypi $PYPI_TOKEN
bash scripts/publish.sh
TIANGOLO_BUILD_PACKAGE: ${{ matrix.package }}
run: python -m build
- name: Publish
uses: pypa/gh-action-pypi-publish@v1.9.0

37
.github/workflows/smokeshow.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: Smokeshow
on:
workflow_run:
workflows: [Test]
types: [completed]
permissions:
statuses: write
jobs:
smokeshow:
if: ${{ github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
steps:
- uses: actions/setup-python@v5
with:
python-version: '3.9'
- run: pip install smokeshow
- uses: actions/download-artifact@v4
with:
name: coverage-html
path: htmlcov
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}
- run: smokeshow upload htmlcov
env:
SMOKESHOW_GITHUB_STATUS_DESCRIPTION: Coverage {coverage-percentage}
SMOKESHOW_GITHUB_COVERAGE_THRESHOLD: 95
SMOKESHOW_GITHUB_CONTEXT: coverage
SMOKESHOW_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SMOKESHOW_GITHUB_PR_HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
SMOKESHOW_AUTH_KEY: ${{ secrets.SMOKESHOW_AUTH_KEY }}

53
.github/workflows/test-redistribute.yml vendored Normal file
View File

@@ -0,0 +1,53 @@
name: Test Redistribute
on:
push:
branches:
- main
pull_request:
types:
- opened
- synchronize
jobs:
test-redistribute:
runs-on: ubuntu-latest
strategy:
matrix:
package:
- sqlmodel
- sqlmodel-slim
steps:
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.10"
- name: Install build dependencies
run: pip install build
- name: Build source distribution
env:
TIANGOLO_BUILD_PACKAGE: ${{ matrix.package }}
run: python -m build --sdist
- name: Decompress source distribution
run: |
cd dist
tar xvf sqlmodel*.tar.gz
- name: Install test dependencies
run: |
cd dist/sqlmodel*/
pip install -r requirements-tests.txt
env:
TIANGOLO_BUILD_PACKAGE: ${{ matrix.package }}
- name: Run source distribution tests
run: |
cd dist/sqlmodel*/
bash scripts/test.sh
- name: Build wheel distribution
run: |
cd dist
pip wheel --no-deps sqlmodel*.tar.gz

View File

@@ -5,58 +5,117 @@ on:
branches:
- main
pull_request:
types: [opened, synchronize]
types:
- opened
- synchronize
workflow_dispatch:
inputs:
debug_enabled:
description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
required: false
default: false
default: 'false'
schedule:
# cron every week on monday
- cron: "0 0 * * 1"
jobs:
test:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.6.15", "3.7", "3.8", "3.9", "3.10"]
python-version:
- "3.7"
- "3.8"
- "3.9"
- "3.10"
- "3.11"
- "3.12"
pydantic-version:
- pydantic-v1
- pydantic-v2
fail-fast: false
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
# Allow debugging with tmate
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled }}
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.debug_enabled == 'true' }}
with:
limit-access-to-actor: true
- uses: actions/cache@v2
- uses: actions/cache@v4
id: cache
with:
path: ${{ env.pythonLocation }}
key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml') }}-root
- name: Install poetry
if: steps.cache.outputs.cache-hit != 'true'
# TODO: remove python -m pip install --force git+https://github.com/python-poetry/poetry-core.git@ad33bc2
# once there's a release of Poetry 1.2.x including poetry-core > 1.1.0a6
# Ref: https://github.com/python-poetry/poetry-core/pull/188
run: |
python -m pip install --upgrade pip
python -m pip install --force git+https://github.com/python-poetry/poetry-core.git@ad33bc2
python -m pip install "poetry==1.2.0a2"
python -m poetry plugin add poetry-version-plugin
- name: Configure poetry
run: python -m poetry config virtualenvs.create false
key: ${{ runner.os }}-python-${{ env.pythonLocation }}-${{ hashFiles('pyproject.toml', 'requirements-tests.txt') }}-v01
- name: Install Dependencies
if: steps.cache.outputs.cache-hit != 'true'
run: python -m poetry install
run: pip install -r requirements-tests.txt
- name: Install Pydantic v1
if: matrix.pydantic-version == 'pydantic-v1'
run: pip install --upgrade "pydantic>=1.10.0,<2.0.0"
- name: Install Pydantic v2
if: matrix.pydantic-version == 'pydantic-v2'
run: pip install --upgrade "pydantic>=2.0.2,<3.0.0" "typing-extensions==4.6.1"
- name: Lint
if: ${{ matrix.python-version != '3.6.15' }}
run: python -m poetry run bash scripts/lint.sh
# Do not run on Python 3.7 as mypy behaves differently
if: matrix.python-version != '3.7' && matrix.pydantic-version == 'pydantic-v2'
run: bash scripts/lint.sh
- run: mkdir coverage
- name: Test
run: python -m poetry run bash scripts/test.sh
- name: Upload coverage
uses: codecov/codecov-action@v2
run: bash scripts/test.sh
env:
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}
CONTEXT: ${{ runner.os }}-py${{ matrix.python-version }}
- name: Store coverage files
uses: actions/upload-artifact@v4
with:
name: coverage-${{ matrix.python-version }}-${{ matrix.pydantic-version }}
path: coverage
coverage-combine:
needs:
- test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.8'
- name: Get coverage files
uses: actions/download-artifact@v4
with:
pattern: coverage-*
path: coverage
merge-multiple: true
- run: pip install coverage[toml]
- run: ls -la coverage
- run: coverage combine coverage
- run: coverage report
- run: coverage html --show-contexts --title "Coverage for ${{ github.sha }}"
- name: Store coverage HTML
uses: actions/upload-artifact@v4
with:
name: coverage-html
path: htmlcov
# https://github.com/marketplace/actions/alls-green#why
alls-green: # This job does nothing and is only used for the branch protection
if: always()
needs:
- coverage-combine
runs-on: ubuntu-latest
steps:
- name: Decide whether the needed jobs succeeded or failed
uses: re-actors/alls-green@release/v1
with:
jobs: ${{ toJSON(needs) }}

2
.gitignore vendored
View File

@@ -7,7 +7,7 @@ poetry.lock
dist
htmlcov
*.egg-info
.coverage
.coverage*
coverage.xml
site
*.db

25
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,25 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
default_language_version:
python: python3.10
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: check-added-large-files
- id: check-toml
- id: check-yaml
args:
- --unsafe
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.5.2
hooks:
- id: ruff
args:
- --fix
- id: ruff-format
ci:
autofix_commit_msg: 🎨 [pre-commit.ci] Auto format from pre-commit.com hooks
autoupdate_commit_msg: ⬆ [pre-commit.ci] pre-commit autoupdate

24
CITATION.cff Normal file
View File

@@ -0,0 +1,24 @@
# This CITATION.cff file was generated with cffinit.
# Visit https://bit.ly/cffinit to generate yours today!
cff-version: 1.2.0
title: SQLModel
message: >-
If you use this software, please cite it using the
metadata from this file.
type: software
authors:
- given-names: Sebastián
family-names: Ramírez
email: tiangolo@gmail.com
identifiers:
repository-code: 'https://github.com/tiangolo/sqlmodel'
url: 'https://sqlmodel.tiangolo.com'
abstract: >-
SQLModel, SQL databases in Python, designed for
simplicity, compatibility, and robustness.
keywords:
- fastapi
- pydantic
- sqlalchemy
license: MIT

View File

@@ -11,9 +11,8 @@
<a href="https://github.com/tiangolo/sqlmodel/actions?query=workflow%3APublish" target="_blank">
<img src="https://github.com/tiangolo/sqlmodel/workflows/Publish/badge.svg" alt="Publish">
</a>
<a href="https://codecov.io/gh/tiangolo/sqlmodel" target="_blank">
<img src="https://img.shields.io/codecov/c/github/tiangolo/sqlmodel?color=%2334D058" alt="Coverage">
</a>
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/tiangolo/sqlmodel" target="_blank">
<img src="https://coverage-badge.samuelcolvin.workers.dev/tiangolo/sqlmodel.svg" alt="Coverage">
<a href="https://pypi.org/project/sqlmodel" target="_blank">
<img src="https://img.shields.io/pypi/v/sqlmodel?color=%2334D058&label=pypi%20package" alt="Package version">
</a>
@@ -39,6 +38,14 @@ The key features are:
* **Extensible**: You have all the power of SQLAlchemy and Pydantic underneath.
* **Short**: Minimize code duplication. A single type annotation does a lot of work. No need to duplicate models in SQLAlchemy and Pydantic.
## Sponsors
<!-- sponsors -->
<a href="https://www.govcert.lu" target="_blank" title="This project is being supported by GOVCERT.LU"><img src="https://sqlmodel.tiangolo.com/img/sponsors/govcert.png"></a>
<!-- /sponsors -->
## SQL Databases in FastAPI
<a href="https://fastapi.tiangolo.com" target="_blank"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" style="width: 20%;"></a>
@@ -51,7 +58,7 @@ It combines SQLAlchemy and Pydantic and tries to simplify the code you write as
## Requirements
A recent and currently supported version of Python (right now, <a href="https://www.python.org/downloads/" class="external-link" target="_blank">Python supports versions 3.6 and above</a>).
A recent and currently supported <a href="https://www.python.org/downloads/" class="external-link" target="_blank">version of Python</a>.
As **SQLModel** is based on **Pydantic** and **SQLAlchemy**, it requires them. They will be automatically installed when you install SQLModel.
@@ -69,7 +76,7 @@ Successfully installed sqlmodel
## Example
For an introduction to databases, SQL, and everything else, see the <a href="https://sqlmodel.tiangolo.com" target="_blank">SQLModel documentation</a>.
For an introduction to databases, SQL, and everything else, see the <a href="https://sqlmodel.tiangolo.com/databases/" target="_blank">SQLModel documentation</a>.
Here's a quick example. ✨

6
data/sponsors.yml Normal file
View File

@@ -0,0 +1,6 @@
gold: []
silver:
- url: https://www.govcert.lu
title: This project is being supported by GOVCERT.LU
img: https://sqlmodel.tiangolo.com/img/sponsors/govcert.png
bronze: []

View File

@@ -19,36 +19,59 @@ In most cases this would probably not be a problem, for example measuring views
## Decimal Types
Pydantic has special support for `Decimal` types using the <a href="https://pydantic-docs.helpmanual.io/usage/types/#arguments-to-condecimal" class="external-link" target="_blank">`condecimal()` special function</a>.
Pydantic has special support for <a href="https://docs.pydantic.dev/latest/api/standard_library_types/#decimaldecimal" class="external-link" target="_blank">`Decimal` types</a>.
!!! tip
Pydantic 1.9, that will be released soon, has improved support for `Decimal` types, without needing to use the `condecimal()` function.
When you use `Decimal` you can specify the number of digits and decimal places to support in the `Field()` function. They will be validated by Pydantic (for example when using FastAPI) and the same information will also be used for the database columns.
But meanwhile, you can already use this feature with `condecimal()` in **SQLModel** it as it's explained here.
/// info
When you use `condecimal()` you can specify the number of digits and decimal places to support. They will be validated by Pydantic (for example when using FastAPI) and the same information will also be used for the database columns.
For the database, **SQLModel** will use <a href="https://docs.sqlalchemy.org/en/20/core/type_basics.html#sqlalchemy.types.DECIMAL" class="external-link" target="_blank">SQLAlchemy's `DECIMAL` type</a>.
!!! info
For the database, **SQLModel** will use <a href="https://docs.sqlalchemy.org/en/14/core/type_basics.html#sqlalchemy.types.DECIMAL" class="external-link" target="_blank">SQLAlchemy's `DECIMAL` type</a>.
///
## Decimals in SQLModel
Let's say that each hero in the database will have an amount of money. We could make that field a `Decimal` type using the `condecimal()` function:
```{.python .annotate hl_lines="12" }
//// tab | Python 3.10+
```python hl_lines="11"
{!./docs_src/advanced/decimal/tutorial001_py310.py[ln:1-11]!}
# More code here later 👇
```
////
//// tab | Python 3.7+
```python hl_lines="12"
{!./docs_src/advanced/decimal/tutorial001.py[ln:1-12]!}
# More code here later 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/advanced/decimal/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/advanced/decimal/tutorial001.py!}
```
</details>
////
///
Here we are saying that `money` can have at most `5` digits with `max_digits`, **this includes the integers** (to the left of the decimal dot) **and the decimals** (to the right of the decimal dot).
@@ -66,19 +89,36 @@ We are also saying that the number of decimal places (to the right of the decima
🚫 But these are all invalid numbers for that `money` field:
* `1.2345`
* This number has more than 3 decimal places.
* This number has more than 3 decimal places.
* `123.234`
* This number has more than 5 digits in total (integer and decimal part).
* This number has more than 5 digits in total (integer and decimal part).
* `123`
* Even though this number doesn't have any decimals, we still have 3 places saved for them, which means that we can **only use 2 places** for the **integer part**, and this number has 3 integer digits. So, the allowed number of integer digits is `max_digits` - `decimal_places` = 2.
* Even though this number doesn't have any decimals, we still have 3 places saved for them, which means that we can **only use 2 places** for the **integer part**, and this number has 3 integer digits. So, the allowed number of integer digits is `max_digits` - `decimal_places` = 2.
!!! tip
Make sure you adjust the number of digits and decimal places for your own needs, in your own application. 🤓
/// tip
Make sure you adjust the number of digits and decimal places for your own needs, in your own application. 🤓
///
## Create models with Decimals
When creating new models you can actually pass normal (`float`) numbers, Pydantic will automatically convert them to `Decimal` types, and **SQLModel** will store them as `Decimal` types in the database (using SQLAlchemy).
//// tab | Python 3.10+
```Python hl_lines="4-6"
# Code above omitted 👆
{!./docs_src/advanced/decimal/tutorial001_py310.py[ln:24-34]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="4-6"
# Code above omitted 👆
@@ -87,19 +127,46 @@ When creating new models you can actually pass normal (`float`) numbers, Pydanti
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/advanced/decimal/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/advanced/decimal/tutorial001.py!}
```
</details>
////
///
## Select Decimal data
Then, when working with Decimal types, you can confirm that they indeed avoid those rounding errors from floats:
//// tab | Python 3.10+
```Python hl_lines="15-16"
# Code above omitted 👆
{!./docs_src/advanced/decimal/tutorial001_py310.py[ln:37-50]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="15-16"
# Code above omitted 👆
@@ -108,14 +175,27 @@ Then, when working with Decimal types, you can confirm that they indeed avoid th
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/advanced/decimal/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/advanced/decimal/tutorial001.py!}
```
</details>
////
///
## Review the results
@@ -142,7 +222,10 @@ Total money: 3.300
</div>
!!! warning
Although Decimal types are supported and used in the Python side, not all databases support it. In particular, SQLite doesn't support decimals, so it will convert them to the same floating `NUMERIC` type it supports.
/// warning
But decimals are supported by most of the other SQL databases. 🎉
Although Decimal types are supported and used in the Python side, not all databases support it. In particular, SQLite doesn't support decimals, so it will convert them to the same floating `NUMERIC` type it supports.
But decimals are supported by most of the other SQL databases. 🎉
///

342
docs/advanced/uuid.md Normal file
View File

@@ -0,0 +1,342 @@
# UUID (Universally Unique Identifiers)
We have discussed some data types like `str`, `int`, etc.
There's another data type called `UUID` (Universally Unique Identifier).
You might have seen **UUIDs**, for example in URLs. They look something like this:
```
4ff2dab7-bffe-414d-88a5-1826b9fea8df
```
UUIDs can be particularly useful as an alternative to auto-incrementing integers for **primary keys**.
/// info
Official support for UUIDs was added in SQLModel version `0.0.20`.
///
## About UUIDs
UUIDs are numbers with 128 bits, that is, 16 bytes.
They are normally seen as 32 <abbr title="numbers in base 16 (instead of base 10), using letters from A to F to represent the numbers from 10 to 15">hexadecimal</abbr> characters separated by dashes.
There are several versions of UUID, some versions include the current time in the bytes, but **UUIDs version 4** are mainly random, the way they are generated makes them virtually **unique**.
### Distributed UUIDs
You could generate one UUID in one computer, and someone else could generate another UUID in another computer, and it would be almost **impossible** for both UUIDs to be the **same**.
This means that you don't have to wait for the DB to generate the ID for you, you can **generate it in code before sending it to the database**, because you can be quite certain it will be unique.
/// note | Technical Details
Because the number of possible UUIDs is so large (2^128), the probability of generating the same UUID version 4 (the random ones) twice is very low.
If you had 103 trillion version 4 UUIDs stored in the database, the probability of generating a duplicated new one is one in a billion. 🤓
///
For the same reason, if you decided to migrate your database, combine it with another database and mix records, etc. you would most probably be able to **just use the same UUIDs** you had originally.
/// warning
There's still a chance you could have a collision, but it's very low. In most cases you could assume you wouldn't have it, but it would be good to be prepared for it.
///
### UUIDs Prevent Information Leakage
Because UUIDs version 4 are **random**, you could give these IDs to the application users or to other systems, **without exposing information** about your application.
When using **auto-incremented integers** for primary keys, you could implicitly expose information about your system. For example, someone could create a new hero, and by getting the hero ID `20` **they would know that you have 20 heroes** in your system (or even less, if some heroes were already deleted).
### UUID Storage
Because UUIDs are 16 bytes, they would **consume more space** in the database than a smaller auto-incremented integer (commonly 4 bytes).
Depending on the database you use, UUIDs could have **better or worse performance**. If you are concerned about that, you should check the documentation for the specific database.
SQLite doesn't have a specific UUID type, so it will store the UUID as a string. Other databases like Postgres have a specific UUID type which would result in better performance and space usage than strings.
## Models with UUIDs
To use UUIDs as primary keys we need to import `uuid`, which is part of the Python standard library (we don't have to install anything) and use `uuid.UUID` as the **type** for the ID field.
We also want the Python code to **generate a new UUID** when creating a new instance, so we use `default_factory`.
The parameter `default_factory` takes a function (or in general, a "<abbr title="Something that can be called as a function.">callable</abbr>"). This function will be **called when creating a new instance** of the model and the value returned by the function will be used as the default value for the field.
For the function in `default_factory` we pass `uuid.uuid4`, which is a function that generates a **new UUID version 4**.
/// tip
We don't call `uuid.uuid4()` ourselves in the code (we don't put the parenthesis). Instead, we pass the function itself, just `uuid.uuid4`, so that SQLModel can call it every time we create a new instance.
///
This means that the UUID will be generated in the Python code, **before sending the data to the database**.
//// tab | Python 3.10+
```Python hl_lines="1 7"
{!./docs_src/advanced/uuid/tutorial001_py310.py[ln:1-10]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="1 8"
{!./docs_src/advanced/uuid/tutorial001.py[ln:1-11]!}
# Code below omitted 👇
```
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/advanced/uuid/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/advanced/uuid/tutorial001.py!}
```
////
///
Pydantic has support for <a href="https://docs.pydantic.dev/latest/api/standard_library_types/#uuid" class="external-link" target="_blank">`UUID` types</a>.
For the database, **SQLModel** internally uses <a href="https://docs.sqlalchemy.org/en/20/core/type_basics.html#sqlalchemy.types.Uuid" class="external-link" target="_blank">SQLAlchemy's `Uuid` type</a>.
### Create a Record with a UUID
When creating a `Hero` record, the `id` field will be **automatically populated** with a new UUID because we set `default_factory=uuid.uuid4`.
As `uuid.uuid4` will be called when creating the model instance, even before sending it to the database, we can **access and use the ID right away**.
And that **same ID (a UUID)** will be saved in the database.
//// tab | Python 3.10+
```Python hl_lines="5 7 9 14"
# Code above omitted 👆
{!./docs_src/advanced/uuid/tutorial001_py310.py[ln:23-34]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="5 7 9 14"
# Code above omitted 👆
{!./docs_src/advanced/uuid/tutorial001.py[ln:24-35]!}
# Code below omitted 👇
```
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/advanced/uuid/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/advanced/uuid/tutorial001.py!}
```
////
///
### Select a Hero
We can do the same operations we could do with other fields.
For example we can **select a hero by ID**:
//// tab | Python 3.10+
```Python hl_lines="15"
# Code above omitted 👆
{!./docs_src/advanced/uuid/tutorial001_py310.py[ln:37-54]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="15"
# Code above omitted 👆
{!./docs_src/advanced/uuid/tutorial001.py[ln:38-55]!}
# Code below omitted 👇
```
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/advanced/uuid/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/advanced/uuid/tutorial001.py!}
```
////
///
/// tip
Even if a database like SQLite stores the UUID as a string, we can select and run comparisons using a Python UUID object and it will work.
SQLModel (actually SQLAlchemy) will take care of making it work. ✨
///
#### Select with `session.get()`
We could also select by ID with `session.get()`:
//// tab | Python 3.10+
```Python hl_lines="15"
# Code above omitted 👆
{!./docs_src/advanced/uuid/tutorial002_py310.py[ln:37-54]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="15"
# Code above omitted 👆
{!./docs_src/advanced/uuid/tutorial002.py[ln:38-55]!}
# Code below omitted 👇
```
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/advanced/uuid/tutorial002_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/advanced/uuid/tutorial002.py!}
```
////
///
The same way as with other fields, we could update, delete, etc. 🚀
### Run the program
If you run the program, you will see the **UUID** generated in the Python code, and then the record **saved in the database with the same UUID**.
<div class="termy">
```console
$ python app.py
// Some boilerplate and previous output omitted 😉
// In SQLite, the UUID will be stored as a string
// other DBs like Postgres have a specific UUID type
CREATE TABLE hero (
id CHAR(32) NOT NULL,
name VARCHAR NOT NULL,
secret_name VARCHAR NOT NULL,
age INTEGER,
PRIMARY KEY (id)
)
// Before saving in the DB we already have the UUID
The hero before saving in the DB
name='Deadpond' secret_name='Dive Wilson' id=UUID('0e44c1a6-88d3-4a35-8b8a-307faa2def28') age=None
The hero ID was already set
0e44c1a6-88d3-4a35-8b8a-307faa2def28
// The SQL statement to insert the record uses our UUID
INSERT INTO hero (id, name, secret_name, age) VALUES (?, ?, ?, ?)
('0e44c1a688d34a358b8a307faa2def28', 'Deadpond', 'Dive Wilson', None)
// And indeed, the record was saved with the UUID we created 😎
After saving in the DB
age=None id=UUID('0e44c1a6-88d3-4a35-8b8a-307faa2def28') name='Deadpond' secret_name='Dive Wilson'
// Now we create a new hero (to select it in a bit)
Created hero:
age=None id=UUID('9d90d186-85db-4eaa-891a-def7b4ae2dab') name='Spider-Boy' secret_name='Pedro Parqueador'
Created hero ID:
9d90d186-85db-4eaa-891a-def7b4ae2dab
// And now we select it
Selected hero:
age=None id=UUID('9d90d186-85db-4eaa-891a-def7b4ae2dab') name='Spider-Boy' secret_name='Pedro Parqueador'
Selected hero ID:
9d90d186-85db-4eaa-891a-def7b4ae2dab
```
</div>
## Learn More
You can learn more about **UUIDs** in:
* The official <a href="https://docs.python.org/3/library/uuid.html" class="external-link" target="_blank">Python docs for UUID</a>.
* The <a href="https://en.wikipedia.org/wiki/Universally_unique_identifier" class="external-link" target="_blank">Wikipedia for UUID</a>.

View File

@@ -6,10 +6,6 @@ First, you might want to see the basic ways to [help SQLModel and get help](help
If you already cloned the repository and you know that you need to deep dive in the code, here are some guidelines to set up your environment.
### Python
SQLModel supports Python 3.6 and above, but for development you should have at least **Python 3.7**.
### Poetry
**SQLModel** uses <a href="https://python-poetry.org/" class="external-link" target="_blank">Poetry</a> to build, package, and publish the project.
@@ -116,7 +112,7 @@ There is a script that you can run locally to test all the code and generate cov
<div class="termy">
```console
$ bash scripts/test-cov-html.sh
$ bash scripts/test.sh
```
</div>

View File

@@ -8,6 +8,10 @@
white-space: pre-wrap;
}
.termy .linenos {
display: none;
}
a.external-link::after {
/* \00A0 is a non-breaking space
to make the mark be on the same line as the link

View File

@@ -1,9 +1,12 @@
# Intro to Databases
!!! info
Are you a seasoned developer and already know everything about databases? 🤓
/// info
Then you can skip to the [Tutorial - User Guide: First Steps](tutorial/index.md){.internal-link target=_blank} right away.
Are you a seasoned developer and already know everything about databases? 🤓
Then you can skip to the [Tutorial - User Guide: First Steps](tutorial/index.md){.internal-link target=_blank} right away.
///
If you don't know everything about databases, here's a quick overview.
@@ -17,8 +20,11 @@ So, what is a database?
A **database** is a system to store and manage data in a structured and very efficient way.
!!! tip
It's very common to abbreviate the word "database" as **"DB"**.
/// tip
It's very common to abbreviate the word "database" as **"DB"**.
///
As there's a lot of information about databases, and it can get very technical and academic, I'll give you a quick overview about some of the main concepts here.
@@ -28,8 +34,11 @@ I'll even tell you a bit about different types of databases, including the ones
When starting to program, it might **not be obvious** why having a database apart from the code for your program is a **good idea**. Let's start with that.
!!! tip
If that's obvious to you, just continue in the next section below. 👇
/// tip
If that's obvious to you, just continue in the next section below. 👇
///
In your code you already have **variables**, **dictionaries**, **lists**, etc. They all store **data** in some way already. Why would you need to have a separate database?
@@ -128,7 +137,7 @@ If we worked with a single table to store our heroes, it could be like this:
<th>id</th><th>name</th><th>secret_name</th><th>age</th><th>team</th><th>headquarters</th>
</tr>
<tr>
<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td><td>Z-Factor</td><td>Sister Margarets Bar</td>
<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td><td>Z-Factor</td><td>Sister Margaret's Bar</td>
</tr>
<tr>
<td>2</td><td>Spider-Boy</td><td>Pedro Parqueador</td><td>null</td><td>Preventers</td><td>Sharp Tower</td>
@@ -157,7 +166,7 @@ We could end up with inconsistent information, having one place saying "Prevente
<th>id</th><th>name</th><th>secret_name</th><th>age</th><th>team</th><th>headquarters</th>
</tr>
<tr>
<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td><td>Z-Force</td><td>Sister Margarets Bar</td>
<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td><td>Z-Force</td><td>Sister Margaret's Bar</td>
</tr>
<tr>
<td>2</td><td>Spider-Boy</td><td>Pedro Parqueador</td><td>null</td><td>Preventers</td><td>Preventers Tower ✅</td>
@@ -176,7 +185,7 @@ We could forget the name of the team and end up adding "Mahjong" with an invalid
<th>id</th><th>name</th><th>secret_name</th><th>age</th><th>team</th><th>headquarters</th>
</tr>
<tr>
<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td><td>Z-Force</td><td>Sister Margarets Bar</td>
<td>1</td><td>Deadpond</td><td>Dive Wilson</td><td>null</td><td>Z-Force</td><td>Sister Margaret's Bar</td>
</tr>
<tr>
<td>2</td><td>Spider-Boy</td><td>Pedro Parqueador</td><td>null</td><td>Preventers</td><td>Preventers Tower</td>
@@ -185,7 +194,7 @@ We could forget the name of the team and end up adding "Mahjong" with an invalid
<td>3</td><td>Rusty-Man</td><td>Tommy Sharp</td><td>48</td><td>Preventers</td><td>Sharp Tower</td>
</tr>
<tr>
<td>4</td><td>Mahjong</td><td>Neena Thurgirl</td><td>31</td><td>Y-Force 🚨</td><td>Sister Margarets Bar</td>
<td>4</td><td>Mahjong</td><td>Neena Thurgirl</td><td>31</td><td>Y-Force 🚨</td><td>Sister Margaret's Bar</td>
</tr>
</table>
@@ -207,7 +216,7 @@ The table for the teams could look like this:
<td>1</td><td>Preventers</td><td>Sharp Tower</td>
</tr>
<tr>
<td>2</td><td>Z-Force</td><td>Sister Margarets Bar</td>
<td>2</td><td>Z-Force</td><td>Sister Margaret's Bar</td>
</tr>
</table>
@@ -308,8 +317,11 @@ Next, it receives the data and puts it in Python objects that you can continue t
I'll tell you more about SQL, SQLModel, how to use them, and how they are related in the next sections.
!!! info "Technical Details"
SQLModel is built on top of SQLAlchemy. It is, in fact, just <a href="https://www.sqlalchemy.org/" class="external-link" target="_blank">SQLAlchemy</a> and <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> mixed together with some sugar on top.
/// info | Technical Details
SQLModel is built on top of SQLAlchemy. It is, in fact, just <a href="https://www.sqlalchemy.org/" class="external-link" target="_blank">SQLAlchemy</a> and <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> mixed together with some sugar on top.
///
## NoSQL Databases

View File

@@ -62,7 +62,7 @@ The user is probably, in some way, telling your application:
2
```
And the would be this table (with a single row):
And the result would be this table (with a single row):
<table>
<tr>
@@ -111,7 +111,7 @@ DROP TABLE hero;
That is how you tell the database in SQL to delete the entire table `hero`.
<a href="http://www.nooooooooooooooo.com/" class="external-link" target="_blank">Nooooo!</a> We lost all the data in the `hero` table! 💥😱
<a href="https://theuselessweb.site/nooooooooooooooo/" class="external-link" target="_blank">Nooooo!</a> We lost all the data in the `hero` table! 💥😱
### SQL Sanitization
@@ -172,8 +172,11 @@ The difference in the final SQL statement is subtle, but it changes the meaning
SELECT * FROM hero WHERE id = "2; DROP TABLE hero;";
```
!!! tip
Notice the double quotes (`"`) making it a string instead of more raw SQL.
/// tip
Notice the double quotes (`"`) making it a string instead of more raw SQL.
///
The database will not find any record with that ID:
@@ -187,8 +190,11 @@ Then your code will continue to execute and calmly tell the user that it couldn'
But we never deleted the `hero` table. 🎉
!!! info
Of course, there are also other ways to do SQL data sanitization without using a tool like **SQLModel**, but it's still a nice feature you get by default.
/// info
Of course, there are also other ways to do SQL data sanitization without using a tool like **SQLModel**, but it's still a nice feature you get by default.
///
### Editor Support
@@ -291,8 +297,11 @@ There are many ORMs available apart from **SQLModel**, you can read more about s
## SQL Table Names
!!! info "Technical Background"
This is a bit of boring background for SQL purists. Feel free to skip this section. 😉
/// info | Technical Background
This is a bit of boring background for SQL purists. Feel free to skip this section. 😉
///
When working with pure SQL, it's common to name the tables in plural. So, the table would be named `heroes` instead of `hero`, because it could contain multiple rows, each with one hero.
@@ -304,5 +313,8 @@ You will see **your own code** a lot more than the internal table names, so it's
So, to keep things consistent, I'll keep using the same table names that **SQLModel** would have generated.
!!! tip
You can also override the table name. You can read about it in the Advanced User Guide.
/// tip
You can also override the table name. You can read about it in the Advanced User Guide.
///

View File

@@ -12,7 +12,7 @@ Nevertheless, SQLModel is completely **independent** of FastAPI and can be used
## Just Modern Python
It's all based on standard <abbr title="Python currently supported versions, 3.6 and above.">modern **Python**</abbr> type annotations. No new syntax to learn. Just standard modern Python.
It's all based on standard <abbr title="Currently supported versions of Python">modern **Python**</abbr> type annotations. No new syntax to learn. Just standard modern Python.
If you need a 2 minute refresher of how to use Python types (even if you don't use SQLModel or FastAPI), check the FastAPI tutorial section: <a href="https://fastapi.tiangolo.com/python-types/" class="external-link" target="_blank">Python types intro</a>.
@@ -36,17 +36,10 @@ You will get completion for everything while writing the **minimum** amount of c
You won't need to keep guessing the types of different attributes in your models, if they could be `None`, etc. Your editor will be able to help you with everything because **SQLModel** is based on **standard Python type annotations**.
**SQLModel** even adopts currently <a href="https://github.com/microsoft/pyright/blob/main/specs/dataclass_transforms.md" class="external-link" target="_blank">in development standards</a> for Python type annotations to ensure the **best developer experience**, so you will get inline errors and autocompletion even while creating new model instances.
**SQLModel** adopts <a href="https://peps.python.org/pep-0681/" class="external-link" target="_blank">PEP 681</a> for Python type annotations to ensure the **best developer experience**, so you will get inline errors and autocompletion even while creating new model instances.
<img class="shadow" src="/img/index/autocompletion01.png">
!!! info
Don't worry, adopting this in-development standard only affects/improves editor support.
It doesn't affect performance or correctness. And if the in-progress standard was deprecated your code won't be affected.
Meanwhile, you will get inline errors (like type checks) and autocompletion on places you wouldn't get with any other library. 🎉
## Short
**SQLModel** has **sensible defaults** for everything, with **optional configurations** everywhere.

View File

@@ -58,26 +58,128 @@ You can:
I love to hear about how **SQLModel** is being used, what you have liked in it, in which project/company are you using it, etc.
## Help others with issues in GitHub
## Help others with questions in GitHub
You can see <a href="https://github.com/tiangolo/sqlmodel/issues" class="external-link" target="_blank">existing issues</a> and try and help others, most of the times they are questions that you might already know the answer for. 🤓
You can try and help others with their questions in:
* <a href="https://github.com/tiangolo/sqlmodel/discussions/categories/questions?discussions_q=category%3AQuestions+is%3Aunanswered" class="external-link" target="_blank">GitHub Discussions</a>
* <a href="https://github.com/tiangolo/sqlmodel/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Aquestion+-label%3Aanswered+" class="external-link" target="_blank">GitHub Issues</a>
In many cases you might already know the answer for those questions. 🤓
Just remember, the most important point is: try to be kind. People come with their frustrations and in many cases don't ask in the best way, but try as best as you can to be kind. 🤗
The idea is for the **SQLModel** community to be kind and welcoming. At the same time, don't accept bullying or disrespectful behavior towards others. We have to take care of each other.
---
Here's how to help others with questions (in discussions or issues):
### Understand the question
* Check if you can understand what is the **purpose** and use case of the person asking.
* Then check if the question (the vast majority are questions) is **clear**.
* In many cases the question asked is about an imaginary solution from the user, but there might be a **better** one. If you can understand the problem and use case better, you might be able to suggest a better **alternative solution**.
* If you can't understand the question, ask for more **details**.
### Reproduce the problem
For most of the cases and most of the questions there's something related to the person's **original code**.
In many cases they will only copy a fragment of the code, but that's not enough to **reproduce the problem**.
* You can ask them to provide a <a href="https://stackoverflow.com/help/minimal-reproducible-example" class="external-link" target="_blank">minimal, reproducible, example</a>, that you can **copy-paste** and run locally to see the same error or behavior they are seeing, or to understand their use case better.
* If you are feeling too generous, you can try to **create an example** like that yourself, just based on the description of the problem. Just have in mind that this might take a lot of time and it might be better to ask them to clarify the problem first.
### Suggest solutions
* After being able to understand the question, you can give them a possible **answer**.
* In many cases, it's better to understand their **underlying problem or use case**, because there might be a better way to solve it than what they are trying to do.
### Ask to close
If they reply, there's a high chance you would have solved their problem, congrats, **you're a hero**! 🦸
* Now, if that solved their problem, you can ask them to:
* In GitHub Discussions: mark the comment as the **answer**.
* In GitHub Issues: **close** the issue**.
## Watch the GitHub repository
You can "watch" SQLModel in GitHub (clicking the "watch" button at the top right): <a href="https://github.com/tiangolo/sqlmodel" class="external-link" target="_blank">https://github.com/tiangolo/sqlmodel</a>. 👀
If you select "Watching" instead of "Releases only" you will receive notifications when someone creates a new issue.
If you select "Watching" instead of "Releases only" you will receive notifications when someone creates a new issue or question. You can also specify that you only want to be notified about new issues, or discussions, or PRs, etc.
Then you can try and help them solve those issues.
Then you can try and help them solve those questions.
## Create issues
## Ask Questions
You can <a href="https://github.com/tiangolo/sqlmodel/issues/new/choose" class="external-link" target="_blank">create a new issue</a> in the GitHub repository, for example to:
You can <a href="https://github.com/tiangolo/sqlmodel/discussions/new?category=questions" class="external-link" target="_blank">create a new question</a> in the GitHub repository, for example to:
* Ask a **question** or ask about a **problem**.
* Suggest a new **feature**.
**Note**: if you create an issue, then I'm going to ask you to also help others. 😉
**Note**: if you do it, then I'm going to ask you to also help others. 😉
## Review Pull Requests
You can help me review pull requests from others.
Again, please try your best to be kind. 🤗
---
Here's what to have in mind and how to review a pull request:
### Understand the problem
* First, make sure you **understand the problem** that the pull request is trying to solve. It might have a longer discussion in a GitHub Discussion or issue.
* There's also a good chance that the pull request is not actually needed because the problem can be solved in a **different way**. Then you can suggest or ask about that.
### Don't worry about style
* Don't worry too much about things like commit message styles, I will squash and merge customizing the commit manually.
* Also don't worry about style rules, there are already automatized tools checking that.
And if there's any other style or consistency need, I'll ask directly for that, or I'll add commits on top with the needed changes.
### Check the code
* Check and read the code, see if it makes sense, **run it locally** and see if it actually solves the problem.
* Then **comment** saying that you did that, that's how I will know you really checked it.
/// info
Unfortunately, I can't simply trust PRs that just have several approvals.
Several times it has happened that there are PRs with 3, 5 or more approvals, probably because the description is appealing, but when I check the PRs, they are actually broken, have a bug, or don't solve the problem they claim to solve. 😅
So, it's really important that you actually read and run the code, and let me know in the comments that you did. 🤓
///
* If the PR can be simplified in a way, you can ask for that, but there's no need to be too picky, there might be a lot of subjective points of view (and I will have my own as well 🙈), so it's better if you can focus on the fundamental things.
### Tests
* Help me check that the PR has **tests**.
* Check that the tests **fail** before the PR. 🚨
* Then check that the tests **pass** after the PR. ✅
* Many PRs don't have tests, you can **remind** them to add tests, or you can even **suggest** some tests yourself. That's one of the things that consume most time and you can help a lot with that.
* Then also comment what you tried, that way I'll know that you checked it. 🤓
## Create a Pull Request
@@ -86,7 +188,47 @@ You can [contribute](contributing.md){.internal-link target=_blank} to the sourc
* To fix a typo you found on the documentation.
* To propose new documentation sections.
* To fix an existing issue/bug.
* Make sure to add tests.
* To add a new feature.
* Make sure to add tests.
* Make sure to add documentation if it's relevant.
## Help Maintain SQLModel
Help me maintain **SQLModel**! 🤓
There's a lot of work to do, and for most of it, **YOU** can do it.
The main tasks that you can do right now are:
* [Help others with questions in GitHub](#help-others-with-questions-in-github){.internal-link target=_blank} (see the section above).
* [Review Pull Requests](#review-pull-requests){.internal-link target=_blank} (see the section above).
Those two tasks are what **consume time the most**. That's the main work of maintaining SQLModel.
If you can help me with that, **you are helping me maintain SQLModel** and making sure it keeps **advancing faster and better**. 🚀
## Join the chat
Join the 👥 <a href="https://discord.gg/VQjSZaeJmf" class="external-link" target="_blank">FastAPI and Friends Discord chat server</a> 👥 and hang out with others in the community. There's a `#sqlmodel` channel.
/// tip
For questions, ask them in <a href="https://github.com/tiangolo/sqlmodel/discussions/new?category=questions" class="external-link" target="_blank">GitHub Discussions</a>, there's a much better chance you will receive help there.
Use the chat only for other general conversations.
///
### Don't use the chat for questions
Have in mind that as chats allow more "free conversation", it's easy to ask questions that are too general and more difficult to answer, so, you might not receive answers.
In GitHub, the template will guide you to write the right question so that you can more easily get a good answer, or even solve the problem yourself even before asking. And in GitHub I can make sure I always answer everything, even if it takes some time. I can't personally do that with the chat. 😅
Conversations in the chat are also not as easily searchable as in GitHub, so questions and answers might get lost in the conversation.
On the other side, there are thousands of users in the chat, so there's a high chance you'll find someone to talk to there, almost all the time. 😄
## Sponsor the author

View File

@@ -90,4 +90,4 @@
</root>
</mxGraphModel>
</diagram>
</mxfile>
</mxfile>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -202,4 +202,4 @@
</root>
</mxGraphModel>
</diagram>
</mxfile>
</mxfile>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -115,7 +115,7 @@
<mxCell id="56" value="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 18px&quot;&gt;Z-Force&lt;/span&gt;" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;top=0;left=0;bottom=0;right=0;overflow=hidden;strokeColor=none;fillColor=none;" parent="54" vertex="1">
<mxGeometry x="50" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="57" value="&lt;p style=&quot;background-color: rgb(255 , 255 , 255) ; line-height: 19px&quot;&gt;&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 18px&quot;&gt;Sister Margarets Bar&lt;/font&gt;&lt;/p&gt;" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;top=0;left=0;bottom=0;right=0;overflow=hidden;strokeColor=none;fillColor=none;" parent="54" vertex="1">
<mxCell id="57" value="&lt;p style=&quot;background-color: rgb(255 , 255 , 255) ; line-height: 19px&quot;&gt;&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 18px&quot;&gt;Sister Margaret's Bar&lt;/font&gt;&lt;/p&gt;" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;top=0;left=0;bottom=0;right=0;overflow=hidden;strokeColor=none;fillColor=none;" parent="54" vertex="1">
<mxGeometry x="160" width="200" height="50" as="geometry"/>
</mxCell>
<mxCell id="66" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;strokeWidth=2;" parent="1" source="18" target="54" edge="1">
@@ -148,4 +148,4 @@
</root>
</mxGraphModel>
</diagram>
</mxfile>
</mxfile>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

View File

@@ -81,4 +81,4 @@
</root>
</mxGraphModel>
</diagram>
</mxfile>
</mxfile>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -34,4 +34,4 @@
</root>
</mxGraphModel>
</diagram>
</mxfile>
</mxfile>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

@@ -61,4 +61,4 @@
</root>
</mxGraphModel>
</diagram>
</mxfile>
</mxfile>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 87 KiB

View File

@@ -94,4 +94,4 @@
</root>
</mxGraphModel>
</diagram>
</mxfile>
</mxfile>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -94,4 +94,4 @@
</root>
</mxGraphModel>
</diagram>
</mxfile>
</mxfile>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -94,4 +94,4 @@
</root>
</mxGraphModel>
</diagram>
</mxfile>
</mxfile>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -97,4 +97,4 @@
</root>
</mxGraphModel>
</diagram>
</mxfile>
</mxfile>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -94,4 +94,4 @@
</root>
</mxGraphModel>
</diagram>
</mxfile>
</mxfile>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -97,4 +97,4 @@
</root>
</mxGraphModel>
</diagram>
</mxfile>
</mxfile>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -97,4 +97,4 @@
</root>
</mxGraphModel>
</diagram>
</mxfile>
</mxfile>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -100,4 +100,4 @@
</root>
</mxGraphModel>
</diagram>
</mxfile>
</mxfile>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -89,4 +89,4 @@
</root>
</mxGraphModel>
</diagram>
</mxfile>
</mxfile>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -103,7 +103,7 @@
<mxCell id="56" value="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 18px&quot;&gt;Z-Force&lt;/span&gt;" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;top=0;left=0;bottom=0;right=0;overflow=hidden;fillColor=#e1d5e7;strokeColor=#9673a6;" parent="54" vertex="1">
<mxGeometry x="50" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="57" value="&lt;p style=&quot;line-height: 19px&quot;&gt;&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 18px&quot;&gt;Sister Margarets Bar&lt;/font&gt;&lt;/p&gt;" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;top=0;left=0;bottom=0;right=0;overflow=hidden;fillColor=#e1d5e7;strokeColor=#9673a6;" parent="54" vertex="1">
<mxCell id="57" value="&lt;p style=&quot;line-height: 19px&quot;&gt;&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 18px&quot;&gt;Sister Margaret's Bar&lt;/font&gt;&lt;/p&gt;" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;top=0;left=0;bottom=0;right=0;overflow=hidden;fillColor=#e1d5e7;strokeColor=#9673a6;" parent="54" vertex="1">
<mxGeometry x="160" width="200" height="50" as="geometry"/>
</mxCell>
<mxCell id="69" value="&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 18px&quot;&gt;heroteamlink&lt;/font&gt;" style="shape=table;html=1;whiteSpace=wrap;startSize=30;container=1;collapsible=0;childLayout=tableLayout;fontStyle=1;align=center;fillColor=#FFFFFF;swimlaneFillColor=#ffffff;" vertex="1" parent="1">
@@ -217,4 +217,4 @@
</root>
</mxGraphModel>
</diagram>
</mxfile>
</mxfile>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 53 KiB

View File

@@ -130,4 +130,4 @@
</root>
</mxGraphModel>
</diagram>
</mxfile>
</mxfile>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View File

@@ -130,4 +130,4 @@
</root>
</mxGraphModel>
</diagram>
</mxfile>
</mxfile>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View File

@@ -130,4 +130,4 @@
</root>
</mxGraphModel>
</diagram>
</mxfile>
</mxfile>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View File

@@ -38,4 +38,4 @@
</root>
</mxGraphModel>
</diagram>
</mxfile>
</mxfile>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 126 KiB

View File

@@ -49,4 +49,4 @@
</root>
</mxGraphModel>
</diagram>
</mxfile>
</mxfile>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 130 KiB

After

Width:  |  Height:  |  Size: 130 KiB

View File

@@ -115,7 +115,7 @@
<mxCell id="56" value="&lt;span style=&quot;font-family: &amp;#34;roboto&amp;#34; ; font-size: 18px&quot;&gt;Z-Force&lt;/span&gt;" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;top=0;left=0;bottom=0;right=0;overflow=hidden;" parent="54" vertex="1">
<mxGeometry x="50" width="110" height="50" as="geometry"/>
</mxCell>
<mxCell id="57" value="&lt;p style=&quot;background-color: rgb(255 , 255 , 255) ; line-height: 19px&quot;&gt;&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 18px&quot;&gt;Sister Margarets Bar&lt;/font&gt;&lt;/p&gt;" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;top=0;left=0;bottom=0;right=0;overflow=hidden;" parent="54" vertex="1">
<mxCell id="57" value="&lt;p style=&quot;background-color: rgb(255 , 255 , 255) ; line-height: 19px&quot;&gt;&lt;font face=&quot;Roboto&quot; data-font-src=&quot;https://fonts.googleapis.com/css?family=Roboto&quot; style=&quot;font-size: 18px&quot;&gt;Sister Margaret's Bar&lt;/font&gt;&lt;/p&gt;" style="shape=partialRectangle;html=1;whiteSpace=wrap;connectable=0;top=0;left=0;bottom=0;right=0;overflow=hidden;" parent="54" vertex="1">
<mxGeometry x="160" width="200" height="50" as="geometry"/>
</mxCell>
<mxCell id="66" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;strokeWidth=2;" parent="1" source="18" target="54" edge="1">
@@ -139,4 +139,4 @@
</root>
</mxGraphModel>
</diagram>
</mxfile>
</mxfile>

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 43 KiB

View File

@@ -1,3 +1,7 @@
<style>
.md-content .md-typeset h1 { display: none; }
</style>
<p align="center">
<a href="https://sqlmodel.tiangolo.com"><img src="https://sqlmodel.tiangolo.com/img/logo-margin/logo-margin-vector.svg" alt="SQLModel"></a>
</p>
@@ -11,9 +15,8 @@
<a href="https://github.com/tiangolo/sqlmodel/actions?query=workflow%3APublish" target="_blank">
<img src="https://github.com/tiangolo/sqlmodel/workflows/Publish/badge.svg" alt="Publish">
</a>
<a href="https://codecov.io/gh/tiangolo/sqlmodel" target="_blank">
<img src="https://img.shields.io/codecov/c/github/tiangolo/sqlmodel?color=%2334D058" alt="Coverage">
</a>
<a href="https://coverage-badge.samuelcolvin.workers.dev/redirect/tiangolo/sqlmodel" target="_blank">
<img src="https://coverage-badge.samuelcolvin.workers.dev/tiangolo/sqlmodel.svg" alt="Coverage">
<a href="https://pypi.org/project/sqlmodel" target="_blank">
<img src="https://img.shields.io/pypi/v/sqlmodel?color=%2334D058&label=pypi%20package" alt="Package version">
</a>
@@ -39,6 +42,21 @@ The key features are:
* **Extensible**: You have all the power of SQLAlchemy and Pydantic underneath.
* **Short**: Minimize code duplication. A single type annotation does a lot of work. No need to duplicate models in SQLAlchemy and Pydantic.
## Sponsors
<!-- sponsors -->
{% if sponsors %}
{% for sponsor in sponsors.gold -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor -%}
{%- for sponsor in sponsors.silver -%}
<a href="{{ sponsor.url }}" target="_blank" title="{{ sponsor.title }}"><img src="{{ sponsor.img }}" style="border-radius:15px"></a>
{% endfor %}
{% endif %}
<!-- /sponsors -->
## SQL Databases in FastAPI
<a href="https://fastapi.tiangolo.com" target="_blank"><img src="https://fastapi.tiangolo.com/img/logo-margin/logo-teal.png" style="width: 20%;"></a>
@@ -51,7 +69,7 @@ It combines SQLAlchemy and Pydantic and tries to simplify the code you write as
## Requirements
A recent and currently supported version of Python (right now, <a href="https://www.python.org/downloads/" class="external-link" target="_blank">Python supports versions 3.6 and above</a>).
A recent and currently supported <a href="https://www.python.org/downloads/" class="external-link" target="_blank">version of Python</a>.
As **SQLModel** is based on **Pydantic** and **SQLAlchemy**, it requires them. They will be automatically installed when you install SQLModel.
@@ -69,7 +87,7 @@ Successfully installed sqlmodel
## Example
For an introduction to databases, SQL, and everything else, see the <a href="https://sqlmodel.tiangolo.com" target="_blank">SQLModel documentation</a>.
For an introduction to databases, SQL, and everything else, see the <a href="https://sqlmodel.tiangolo.com/databases/" target="_blank">SQLModel documentation</a>.
Here's a quick example. ✨

View File

@@ -13,7 +13,7 @@ function setupTermynal() {
function createTermynals() {
document
.querySelectorAll(`.${termynalActivateClass} .highlight`)
.querySelectorAll(`.${termynalActivateClass} .highlight code`)
.forEach(node => {
const text = node.textContent;
const lines = text.split("\n");

View File

@@ -72,14 +72,14 @@ class Termynal {
* Initialise the widget, get lines, clear container and start animation.
*/
init() {
/**
/**
* Calculates width and height of Termynal container.
* If container is empty and lines are dynamically loaded, defaults to browser `auto` or CSS.
*/
*/
const containerStyle = getComputedStyle(this.container);
this.container.style.width = containerStyle.width !== '0px' ?
this.container.style.width = containerStyle.width !== '0px' ?
containerStyle.width : undefined;
this.container.style.minHeight = containerStyle.height !== '0px' ?
this.container.style.minHeight = containerStyle.height !== '0px' ?
containerStyle.height : undefined;
this.container.setAttribute('data-termynal', '');
@@ -138,7 +138,7 @@ class Termynal {
restart.innerHTML = "restart ↻"
return restart
}
generateFinish() {
const finish = document.createElement('a')
finish.onclick = (e) => {
@@ -215,7 +215,7 @@ class Termynal {
/**
* Converts line data objects into line elements.
*
*
* @param {Object[]} lineData - Dynamically loaded lines.
* @param {Object} line - Line data object.
* @returns {Element[]} - Array of line elements.
@@ -231,7 +231,7 @@ class Termynal {
/**
* Helper function for generating attributes string.
*
*
* @param {Object} line - Line data object.
* @returns {string} - String of attributes.
*/

View File

@@ -1,31 +1 @@
{% extends "base.html" %}
{%- block scripts %}
{{ super() }}
<script src="https://cdn.jsdelivr.net/npm/qabot@0.4"></script>
<script>
// This prevents the global search from interfering with qa-bot's internal text input.
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('qa-bot').forEach((x) => {
x.addEventListener('keydown', (event) => {
event.stopPropagation();
});
});
});
</script>
<qa-bot
server="https://tiangolo-sqlmodel.docsqa.jina.ai"
theme="infer"
title="SQLModel Bot"
description="SQLModel, SQL databases in Python, designed for simplicity, compatibility, and robustness."
style="font-size: 0.8rem"
>
<template>
<dl>
<dt>You can ask questions about SQLModel. Try:</dt>
<dd>Which Python version is supported?</dd>
<dd>How SQLModel interacts with the database?</dd>
<dd>How can I link tables?</dd>
</dl>
</template>
</qa-bot>
{%- endblock %}

View File

@@ -2,12 +2,284 @@
## Latest Changes
## 0.0.21
### Features
* ✨ Add support for cascade delete relationships: `cascade_delete`, `ondelete`, and `passive_deletes`. PR [#983](https://github.com/tiangolo/sqlmodel/pull/983) by [@estebanx64](https://github.com/estebanx64).
* New docs at: [Cascade Delete Relationships](https://sqlmodel.tiangolo.com/tutorial/relationship-attributes/cascade-delete-relationships/).
### Docs
* 📝 Update docs . PR [#1003](https://github.com/tiangolo/sqlmodel/pull/1003) by [@alejsdev](https://github.com/alejsdev).
### Internal
* ⬆ Bump actions/cache from 3 to 4. PR [#783](https://github.com/tiangolo/sqlmodel/pull/783) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump cairosvg from 2.7.0 to 2.7.1. PR [#919](https://github.com/tiangolo/sqlmodel/pull/919) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump jinja2 from 3.1.3 to 3.1.4. PR [#974](https://github.com/tiangolo/sqlmodel/pull/974) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump pypa/gh-action-pypi-publish from 1.8.11 to 1.9.0. PR [#987](https://github.com/tiangolo/sqlmodel/pull/987) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump mkdocstrings[python] from 0.23.0 to 0.25.1. PR [#927](https://github.com/tiangolo/sqlmodel/pull/927) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump dorny/paths-filter from 2 to 3. PR [#972](https://github.com/tiangolo/sqlmodel/pull/972) by [@dependabot[bot]](https://github.com/apps/dependabot).
## 0.0.20
### Features
* ✨ Add official UUID support, docs and tests, internally using new SQLAlchemy 2.0 types. Initial PR [#992](https://github.com/tiangolo/sqlmodel/pull/992) by [@estebanx64](https://github.com/estebanx64).
* New docs in the [Advanced User Guide: UUID (Universally Unique Identifiers)](https://sqlmodel.tiangolo.com/advanced/uuid/).
### Docs
* ✏️ Fix internal link in `docs/tutorial/create-db-and-table.md`. PR [#911](https://github.com/tiangolo/sqlmodel/pull/911) by [@tfpgh](https://github.com/tfpgh).
* ✏️ Add missing step in `create-db-and-table-with-db-browser.md`. PR [#976](https://github.com/tiangolo/sqlmodel/pull/976) by [@alejsdev](https://github.com/alejsdev).
* ✏️ Fix typo in `docs/tutorial`. PR [#943](https://github.com/tiangolo/sqlmodel/pull/943) by [@luco17](https://github.com/luco17).
* ✏️ Fix typo in `sqlmodel/_compat.py`. PR [#950](https://github.com/tiangolo/sqlmodel/pull/950) by [@Highfire1](https://github.com/Highfire1).
* ✏️ Update pip installation command in tutorial. PR [#975](https://github.com/tiangolo/sqlmodel/pull/975) by [@alejsdev](https://github.com/alejsdev).
* ✏️ Fix typo in `docs/tutorial/relationship-attributes/index.md`. PR [#880](https://github.com/tiangolo/sqlmodel/pull/880) by [@UncleGoogle](https://github.com/UncleGoogle).
### Internal
* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#979](https://github.com/tiangolo/sqlmodel/pull/979) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).
* 🔨 Update docs Termynal scripts to not include line nums for local dev. PR [#1018](https://github.com/tiangolo/sqlmodel/pull/1018) by [@tiangolo](https://github.com/tiangolo).
## 0.0.19
### Fixes
* 🐛 Fix pydantic `EmailStr` support and `max_length` in several String subclasses. PR [#966](https://github.com/tiangolo/sqlmodel/pull/966) by [@estebanx64](https://github.com/estebanx64).
* 🐛 Fix set varchar limit when `max_length` is set on Pydantic models using Pydantic v2. PR [#963](https://github.com/tiangolo/sqlmodel/pull/963) by [@estebanx64](https://github.com/estebanx64).
### Refactors
* ♻️ Refactor generate select template to isolate templated code to the minimum. PR [#967](https://github.com/tiangolo/sqlmodel/pull/967) by [@tiangolo](https://github.com/tiangolo).
### Upgrades
* ⬆️ Update minimum SQLAlchemy version to 2.0.14 as that one includes `TryCast` used internally. PR [#964](https://github.com/tiangolo/sqlmodel/pull/964) by [@tiangolo](https://github.com/tiangolo).
### Docs
* ✏️ Fix broken link to `@dataclass_transform` (now PEP 681) in `docs/features.md`. PR [#753](https://github.com/tiangolo/sqlmodel/pull/753) by [@soof-golan](https://github.com/soof-golan).
### Internal
* ⬆️ Upgrade Ruff and Black. PR [#968](https://github.com/tiangolo/sqlmodel/pull/968) by [@tiangolo](https://github.com/tiangolo).
* ⬆ Bump tiangolo/issue-manager from 0.4.1 to 0.5.0. PR [#922](https://github.com/tiangolo/sqlmodel/pull/922) by [@dependabot[bot]](https://github.com/apps/dependabot).
* 📌 Pin typing-extensions in tests for compatiblity with Python 3.8, dirty-equals, Pydantic. PR [#965](https://github.com/tiangolo/sqlmodel/pull/965) by [@tiangolo](https://github.com/tiangolo).
* 👷 Update GitHub Actions to download and upload artifacts. PR [#936](https://github.com/tiangolo/sqlmodel/pull/936) by [@tiangolo](https://github.com/tiangolo).
* 👷 Tweak CI for test-redistribute, add needed env vars for slim. PR [#929](https://github.com/tiangolo/sqlmodel/pull/929) by [@tiangolo](https://github.com/tiangolo).
## 0.0.18
### Internal
* ✨ Add `sqlmodel-slim` setup. PR [#916](https://github.com/tiangolo/sqlmodel/pull/916) by [@tiangolo](https://github.com/tiangolo).
In the future SQLModel will include the standard default recommended packages, and `sqlmodel-slim` will come without those recommended standard packages and with a group of optional dependencies `sqlmodel-slim[standard]`, equivalent to `sqlmodel`, for those that want to opt out of those packages.
* 🔧 Re-enable MkDocs Material Social plugin. PR [#915](https://github.com/tiangolo/sqlmodel/pull/915) by [@tiangolo](https://github.com/tiangolo).
## 0.0.17
### Refactors
* ♻️ Refactor types to properly support Pydantic 2.7. PR [#913](https://github.com/tiangolo/sqlmodel/pull/913) by [@tiangolo](https://github.com/tiangolo).
### Docs
* 📝 Update ModelRead to ModelPublic documentation and examples. PR [#885](https://github.com/tiangolo/sqlmodel/pull/885) by [@estebanx64](https://github.com/estebanx64).
* ✨ Add source examples for Python 3.10 and 3.9 with updated syntax. PR [#842](https://github.com/tiangolo/sqlmodel/pull/842) by [@tiangolo](https://github.com/tiangolo) and [@estebanx64](https://github.com/estebanx64).
### Internal
* ⬆ Bump actions/setup-python from 4 to 5. PR [#733](https://github.com/tiangolo/sqlmodel/pull/733) by [@dependabot[bot]](https://github.com/apps/dependabot).
* 🔨 Update internal scripts and remove unused ones. PR [#914](https://github.com/tiangolo/sqlmodel/pull/914) by [@tiangolo](https://github.com/tiangolo).
* 🔧 Migrate from Poetry to PDM for the internal build config. PR [#912](https://github.com/tiangolo/sqlmodel/pull/912) by [@tiangolo](https://github.com/tiangolo).
* 🔧 Update MkDocs, disable cards while I can upgrade to the latest MkDocs Material, that fixes an issue with social cards. PR [#888](https://github.com/tiangolo/sqlmodel/pull/888) by [@tiangolo](https://github.com/tiangolo).
* 👷 Add cron to run test once a week on monday. PR [#869](https://github.com/tiangolo/sqlmodel/pull/869) by [@estebanx64](https://github.com/estebanx64).
* ⬆️ Upgrade Ruff version and configs. PR [#859](https://github.com/tiangolo/sqlmodel/pull/859) by [@tiangolo](https://github.com/tiangolo).
* 🔥 Remove Jina QA Bot as it has been discontinued. PR [#840](https://github.com/tiangolo/sqlmodel/pull/840) by [@tiangolo](https://github.com/tiangolo).
## 0.0.16
### Features
* ✨ Add new method `.sqlmodel_update()` to update models in place, including an `update` parameter for extra data. And fix implementation for the (now documented) `update` parameter for `.model_validate()`. PR [#804](https://github.com/tiangolo/sqlmodel/pull/804) by [@tiangolo](https://github.com/tiangolo).
* Updated docs: [Update Data with FastAPI](https://sqlmodel.tiangolo.com/tutorial/fastapi/update/).
* New docs: [Update with Extra Data (Hashed Passwords) with FastAPI](https://sqlmodel.tiangolo.com/tutorial/fastapi/update-extra-data/).
## 0.0.15
### Fixes
* 🐛 Fix class initialization compatibility with Pydantic and SQLModel, fixing errors revealed by the latest Pydantic. PR [#807](https://github.com/tiangolo/sqlmodel/pull/807) by [@tiangolo](https://github.com/tiangolo).
### Internal
* ⬆ Bump tiangolo/issue-manager from 0.4.0 to 0.4.1. PR [#775](https://github.com/tiangolo/sqlmodel/pull/775) by [@dependabot[bot]](https://github.com/apps/dependabot).
* 👷 Fix GitHub Actions build docs filter paths for GitHub workflows. PR [#738](https://github.com/tiangolo/sqlmodel/pull/738) by [@tiangolo](https://github.com/tiangolo).
## 0.0.14
### Features
* ✨ Add support for Pydantic v2 (while keeping support for v1 if v2 is not available). PR [#722](https://github.com/tiangolo/sqlmodel/pull/722) by [@tiangolo](https://github.com/tiangolo) including initial work in PR [#699](https://github.com/tiangolo/sqlmodel/pull/699) by [@AntonDeMeester](https://github.com/AntonDeMeester).
## 0.0.13
### Fixes
* ♻️ Refactor type generation of selects re-order to prioritize models to optimize editor support. PR [#718](https://github.com/tiangolo/sqlmodel/pull/718) by [@tiangolo](https://github.com/tiangolo).
### Refactors
* 🔇 Do not raise deprecation warnings for execute as it's automatically used internally. PR [#716](https://github.com/tiangolo/sqlmodel/pull/716) by [@tiangolo](https://github.com/tiangolo).
* ✅ Move OpenAPI tests inline to simplify updating them with Pydantic v2. PR [#709](https://github.com/tiangolo/sqlmodel/pull/709) by [@tiangolo](https://github.com/tiangolo).
### Upgrades
* ⬆️ Add support for Python 3.11 and Python 3.12. PR [#710](https://github.com/tiangolo/sqlmodel/pull/710) by [@tiangolo](https://github.com/tiangolo).
### Docs
* ✏️ Fix typo, simplify single quote/apostrophe character in "Sister Margaret's" everywhere in the docs. PR [#721](https://github.com/tiangolo/sqlmodel/pull/721) by [@tiangolo](https://github.com/tiangolo).
* 📝 Update docs for Decimal, use proper types. PR [#719](https://github.com/tiangolo/sqlmodel/pull/719) by [@tiangolo](https://github.com/tiangolo).
* 📝 Add source examples for Python 3.9 and 3.10. PR [#715](https://github.com/tiangolo/sqlmodel/pull/715) by [@tiangolo](https://github.com/tiangolo).
### Internal
* 🙈 Update gitignore, include all coverage files. PR [#711](https://github.com/tiangolo/sqlmodel/pull/711) by [@tiangolo](https://github.com/tiangolo).
* 🔧 Update config with new pymdown extensions. PR [#712](https://github.com/tiangolo/sqlmodel/pull/712) by [@tiangolo](https://github.com/tiangolo).
* 🔧 Update docs build setup, add support for sponsors, add sponsor GOVCERT.LU. PR [#720](https://github.com/tiangolo/sqlmodel/pull/720) by [@tiangolo](https://github.com/tiangolo).
* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#697](https://github.com/tiangolo/sqlmodel/pull/697) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).
* 🔧 Show line numbers in docs during local development. PR [#714](https://github.com/tiangolo/sqlmodel/pull/714) by [@tiangolo](https://github.com/tiangolo).
* 📝 Update details syntax with new pymdown extensions format. PR [#713](https://github.com/tiangolo/sqlmodel/pull/713) by [@tiangolo](https://github.com/tiangolo).
## 0.0.12
### Features
* ✨ Upgrade SQLAlchemy to 2.0. PR [#700](https://github.com/tiangolo/sqlmodel/pull/700) by [@tiangolo](https://github.com/tiangolo) including initial work in PR [#563](https://github.com/tiangolo/sqlmodel/pull/563) by [@farahats9](https://github.com/farahats9).
### Internal
* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#686](https://github.com/tiangolo/sqlmodel/pull/686) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).
* 👷 Upgrade latest-changes GitHub Action. PR [#693](https://github.com/tiangolo/sqlmodel/pull/693) by [@tiangolo](https://github.com/tiangolo).
## 0.0.11
### Features
* ✨ Add support for passing a custom SQLAlchemy type to `Field()` with `sa_type`. PR [#505](https://github.com/tiangolo/sqlmodel/pull/505) by [@maru0123-2004](https://github.com/maru0123-2004).
* You might consider this a breaking change if you were using an incompatible combination of arguments, those arguments were not taking effect and now you will have a type error and runtime error telling you that.
* ✨ Do not allow invalid combinations of field parameters for columns and relationships, `sa_column` excludes `sa_column_args`, `primary_key`, `nullable`, etc. PR [#681](https://github.com/tiangolo/sqlmodel/pull/681) by [@tiangolo](https://github.com/tiangolo).
### Docs
* 🎨 Update inline source examples, hide `#` in annotations (from MkDocs Material). PR [#677](https://github.com/tiangolo/sqlmodel/pull/677) by [@Matthieu-LAURENT39](https://github.com/Matthieu-LAURENT39).
### Internal
* ⬆ Update coverage requirement from ^6.2 to >=6.2,<8.0. PR [#663](https://github.com/tiangolo/sqlmodel/pull/663) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Update mkdocs-material requirement from 9.1.21 to 9.2.7. PR [#675](https://github.com/tiangolo/sqlmodel/pull/675) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆️ Upgrade mypy manually. PR [#684](https://github.com/tiangolo/sqlmodel/pull/684) by [@tiangolo](https://github.com/tiangolo).
* ⬆ Update black requirement from ^22.10.0 to >=22.10,<24.0. PR [#664](https://github.com/tiangolo/sqlmodel/pull/664) by [@dependabot[bot]](https://github.com/apps/dependabot).
* 👷 Update CI to build MkDocs Insiders only when the secrets are available, for Dependabot. PR [#683](https://github.com/tiangolo/sqlmodel/pull/683) by [@tiangolo](https://github.com/tiangolo).
## 0.0.10
### Features
* ✨ Add support for all `Field` parameters from Pydantic `1.9.0` and above, make Pydantic `1.9.0` the minimum required version. PR [#440](https://github.com/tiangolo/sqlmodel/pull/440) by [@daniil-berg](https://github.com/daniil-berg).
### Internal
* 🔧 Adopt Ruff for formatting. PR [#679](https://github.com/tiangolo/sqlmodel/pull/679) by [@tiangolo](https://github.com/tiangolo).
## 0.0.9
### Breaking Changes
* 🗑️ Deprecate Python 3.6 and upgrade Poetry and Poetry Version Plugin. PR [#627](https://github.com/tiangolo/sqlmodel/pull/627) by [@tiangolo](https://github.com/tiangolo).
### Features
* ✨ Raise a more clear error when a type is not valid. PR [#425](https://github.com/tiangolo/sqlmodel/pull/425) by [@ddanier](https://github.com/ddanier).
### Fixes
* 🐛 Fix `AsyncSession` type annotations for `exec()`. PR [#58](https://github.com/tiangolo/sqlmodel/pull/58) by [@Bobronium](https://github.com/Bobronium).
* 🐛 Fix allowing using a `ForeignKey` directly, remove repeated column construction from `SQLModelMetaclass.__init__` and upgrade minimum SQLAlchemy to `>=1.4.36`. PR [#443](https://github.com/tiangolo/sqlmodel/pull/443) by [@daniil-berg](https://github.com/daniil-berg).
* 🐛 Fix enum type checks ordering in `get_sqlalchemy_type`. PR [#669](https://github.com/tiangolo/sqlmodel/pull/669) by [@tiangolo](https://github.com/tiangolo).
* 🐛 Fix SQLAlchemy version 1.4.36 breaks SQLModel relationships (#315). PR [#461](https://github.com/tiangolo/sqlmodel/pull/461) by [@byrman](https://github.com/byrman).
### Upgrades
* ⬆️ Upgrade support for SQLAlchemy 1.4.49, update tests. PR [#519](https://github.com/tiangolo/sqlmodel/pull/519) by [@sandrotosi](https://github.com/sandrotosi).
* ⬆ Raise SQLAlchemy version requirement to at least `1.4.29` (related to #434). PR [#439](https://github.com/tiangolo/sqlmodel/pull/439) by [@daniil-berg](https://github.com/daniil-berg).
### Docs
* 📝 Clarify description of in-memory SQLite database in `docs/tutorial/create-db-and-table.md`. PR [#601](https://github.com/tiangolo/sqlmodel/pull/601) by [@SimonCW](https://github.com/SimonCW).
* 📝 Tweak wording in `docs/tutorial/fastapi/multiple-models.md`. PR [#674](https://github.com/tiangolo/sqlmodel/pull/674) by [@tiangolo](https://github.com/tiangolo).
* ✏️ Fix contributing instructions to run tests, update script name. PR [#634](https://github.com/tiangolo/sqlmodel/pull/634) by [@PookieBuns](https://github.com/PookieBuns).
* 📝 Update link to docs for intro to databases. PR [#593](https://github.com/tiangolo/sqlmodel/pull/593) by [@abenezerBelachew](https://github.com/abenezerBelachew).
* 📝 Update docs, use `offset` in example with `limit` and `where`. PR [#273](https://github.com/tiangolo/sqlmodel/pull/273) by [@jbmchuck](https://github.com/jbmchuck).
* 📝 Fix docs for Pydantic's fields using `le` (`lte` is invalid, use `le` ). PR [#207](https://github.com/tiangolo/sqlmodel/pull/207) by [@jrycw](https://github.com/jrycw).
* 📝 Update outdated link in `docs/db-to-code.md`. PR [#649](https://github.com/tiangolo/sqlmodel/pull/649) by [@MatveyF](https://github.com/MatveyF).
* ✏️ Fix typos found with codespell. PR [#520](https://github.com/tiangolo/sqlmodel/pull/520) by [@kianmeng](https://github.com/kianmeng).
* 📝 Fix typos (duplication) in main page. PR [#631](https://github.com/tiangolo/sqlmodel/pull/631) by [@Mr-DRP](https://github.com/Mr-DRP).
* 📝 Update release notes, add second author to PR. PR [#429](https://github.com/tiangolo/sqlmodel/pull/429) by [@br-follow](https://github.com/br-follow).
* 📝 Update instructions about how to make a foreign key required in `docs/tutorial/relationship-attributes/define-relationships-attributes.md`. PR [#474](https://github.com/tiangolo/sqlmodel/pull/474) by [@jalvaradosegura](https://github.com/jalvaradosegura).
* 📝 Update help SQLModel docs. PR [#548](https://github.com/tiangolo/sqlmodel/pull/548) by [@tiangolo](https://github.com/tiangolo).
* ✏️ Fix typo in internal function name `get_sqlachemy_type()`. PR [#496](https://github.com/tiangolo/sqlmodel/pull/496) by [@cmarqu](https://github.com/cmarqu).
* ✏️ Fix typo in docs. PR [#446](https://github.com/tiangolo/sqlmodel/pull/446) by [@davidbrochart](https://github.com/davidbrochart).
* ✏️ Fix typo in `docs/tutorial/create-db-and-table.md`. PR [#477](https://github.com/tiangolo/sqlmodel/pull/477) by [@FluffyDietEngine](https://github.com/FluffyDietEngine).
* ✏️ Fix small typos in docs. PR [#481](https://github.com/tiangolo/sqlmodel/pull/481) by [@micuffaro](https://github.com/micuffaro).
### Internal
* ⬆ [pre-commit.ci] pre-commit autoupdate. PR [#672](https://github.com/tiangolo/sqlmodel/pull/672) by [@pre-commit-ci[bot]](https://github.com/apps/pre-commit-ci).
* ⬆ Bump dawidd6/action-download-artifact from 2.24.2 to 2.28.0. PR [#660](https://github.com/tiangolo/sqlmodel/pull/660) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ✅ Refactor OpenAPI FastAPI tests to simplify updating them later, this moves things around without changes. PR [#671](https://github.com/tiangolo/sqlmodel/pull/671) by [@tiangolo](https://github.com/tiangolo).
* ⬆ Bump actions/checkout from 3 to 4. PR [#670](https://github.com/tiangolo/sqlmodel/pull/670) by [@dependabot[bot]](https://github.com/apps/dependabot).
* 🔧 Update mypy config, use `strict = true` instead of manual configs. PR [#428](https://github.com/tiangolo/sqlmodel/pull/428) by [@michaeloliverx](https://github.com/michaeloliverx).
* ⬆️ Upgrade MkDocs Material. PR [#668](https://github.com/tiangolo/sqlmodel/pull/668) by [@tiangolo](https://github.com/tiangolo).
* 🎨 Update docs format and references with pre-commit and Ruff. PR [#667](https://github.com/tiangolo/sqlmodel/pull/667) by [@tiangolo](https://github.com/tiangolo).
* 🎨 Run pre-commit on all files and autoformat. PR [#666](https://github.com/tiangolo/sqlmodel/pull/666) by [@tiangolo](https://github.com/tiangolo).
* 👷 Move to Ruff and add pre-commit. PR [#661](https://github.com/tiangolo/sqlmodel/pull/661) by [@tiangolo](https://github.com/tiangolo).
* 🛠️ Add `CITATION.cff` file for academic citations. PR [#13](https://github.com/tiangolo/sqlmodel/pull/13) by [@sugatoray](https://github.com/sugatoray).
* 👷 Update docs deployments to Cloudflare. PR [#630](https://github.com/tiangolo/sqlmodel/pull/630) by [@tiangolo](https://github.com/tiangolo).
* 👷‍♂️ Upgrade CI for docs. PR [#628](https://github.com/tiangolo/sqlmodel/pull/628) by [@tiangolo](https://github.com/tiangolo).
* 👷 Update CI debug mode with Tmate. PR [#629](https://github.com/tiangolo/sqlmodel/pull/629) by [@tiangolo](https://github.com/tiangolo).
* 👷 Update latest changes token. PR [#616](https://github.com/tiangolo/sqlmodel/pull/616) by [@tiangolo](https://github.com/tiangolo).
* ⬆️ Upgrade analytics. PR [#558](https://github.com/tiangolo/sqlmodel/pull/558) by [@tiangolo](https://github.com/tiangolo).
* 🔧 Update new issue chooser to point to GitHub Discussions. PR [#546](https://github.com/tiangolo/sqlmodel/pull/546) by [@tiangolo](https://github.com/tiangolo).
* 🔧 Add template for GitHub Discussion questions and update issues template. PR [#544](https://github.com/tiangolo/sqlmodel/pull/544) by [@tiangolo](https://github.com/tiangolo).
* 👷 Refactor CI artifact upload/download for docs previews. PR [#514](https://github.com/tiangolo/sqlmodel/pull/514) by [@tiangolo](https://github.com/tiangolo).
* ⬆ Bump actions/cache from 2 to 3. PR [#497](https://github.com/tiangolo/sqlmodel/pull/497) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump dawidd6/action-download-artifact from 2.24.0 to 2.24.2. PR [#493](https://github.com/tiangolo/sqlmodel/pull/493) by [@dependabot[bot]](https://github.com/apps/dependabot).
* 🔧 Update Smokeshow coverage threshold. PR [#487](https://github.com/tiangolo/sqlmodel/pull/487) by [@tiangolo](https://github.com/tiangolo).
* 👷 Move from Codecov to Smokeshow. PR [#486](https://github.com/tiangolo/sqlmodel/pull/486) by [@tiangolo](https://github.com/tiangolo).
* ⬆ Bump actions/setup-python from 2 to 4. PR [#411](https://github.com/tiangolo/sqlmodel/pull/411) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Update black requirement from ^21.5-beta.1 to ^22.10.0. PR [#460](https://github.com/tiangolo/sqlmodel/pull/460) by [@dependabot[bot]](https://github.com/apps/dependabot).
* Add extra dev dependencies for MkDocs Material. PR [#485](https://github.com/tiangolo/sqlmodel/pull/485) by [@tiangolo](https://github.com/tiangolo).
* ⬆ Update mypy requirement from 0.930 to 0.971. PR [#380](https://github.com/tiangolo/sqlmodel/pull/380) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Update coverage requirement from ^5.5 to ^6.2. PR [#171](https://github.com/tiangolo/sqlmodel/pull/171) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump codecov/codecov-action from 2 to 3. PR [#415](https://github.com/tiangolo/sqlmodel/pull/415) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump actions/upload-artifact from 2 to 3. PR [#412](https://github.com/tiangolo/sqlmodel/pull/412) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Update flake8 requirement from ^3.9.2 to ^5.0.4. PR [#396](https://github.com/tiangolo/sqlmodel/pull/396) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Update pytest requirement from ^6.2.4 to ^7.0.1. PR [#242](https://github.com/tiangolo/sqlmodel/pull/242) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump actions/checkout from 2 to 3.1.0. PR [#458](https://github.com/tiangolo/sqlmodel/pull/458) by [@dependabot[bot]](https://github.com/apps/dependabot).
* ⬆ Bump dawidd6/action-download-artifact from 2.9.0 to 2.24.0. PR [#470](https://github.com/tiangolo/sqlmodel/pull/470) by [@dependabot[bot]](https://github.com/apps/dependabot).
* 👷 Update Dependabot config. PR [#484](https://github.com/tiangolo/sqlmodel/pull/484) by [@tiangolo](https://github.com/tiangolo).
## 0.0.8
### Fixes
* 🐛 Fix auto detecting and setting `nullable`, allowing overrides in field. PR [#423](https://github.com/tiangolo/sqlmodel/pull/423) by [@JonasKs](https://github.com/JonasKs).
* 🐛 Fix auto detecting and setting `nullable`, allowing overrides in field. PR [#423](https://github.com/tiangolo/sqlmodel/pull/423) by [@JonasKs](https://github.com/JonasKs) and [@br-follow](https://github.com/br-follow).
* ♻️ Update `expresion.py`, sync from Jinja2 template, implement `inherit_cache` to solve errors like: `SAWarning: Class SelectOfScalar will not make use of SQL compilation caching`. PR [#422](https://github.com/tiangolo/sqlmodel/pull/422) by [@tiangolo](https://github.com/tiangolo).
### Docs

View File

@@ -4,7 +4,21 @@ In the previous chapter, we saw how to add rows to the database using **SQLModel
Now let's talk a bit about why the `id` field **can't be `NULL`** on the database because it's a **primary key**, and we declare it using `Field(primary_key=True)`.
But the same `id` field actually **can be `None`** in the Python code, so we declare the type with `Optional[int]`, and set the default value to `Field(default=None)`:
But the same `id` field actually **can be `None`** in the Python code, so we declare the type with `int | None (or Optional[int])`, and set the default value to `Field(default=None)`:
//// tab | Python 3.10+
```Python hl_lines="4"
# Code above omitted 👆
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001_py310.py[ln:4-8]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="4"
# Code above omitted 👆
@@ -14,14 +28,27 @@ But the same `id` field actually **can be `None`** in the Python code, so we dec
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py!}
```
</details>
////
///
Next, I'll show you a bit more about the synchronization of data between the database and the Python code.
@@ -31,22 +58,49 @@ When do we get an actual `int` from the database in that `id` field? Let's see a
When we create a new `Hero` instance, we don't set the `id`:
//// tab | Python 3.10+
```Python hl_lines="3-6"
# Code above omitted 👆
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001_py310.py[ln:21-24]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="3-6"
# Code above omitted 👆
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py[ln:23-26]!}
# Code below ommitted 👇
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py!}
```
</details>
////
///
### How `Optional` Helps
@@ -74,6 +128,20 @@ But by declaring it with `Optional[int]`, the editor will help us to avoid writi
We can confirm that by printing our heroes before adding them to the database:
//// tab | Python 3.10+
```Python hl_lines="9-11"
# Code above omitted 👆
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001_py310.py[ln:21-29]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="9-11"
# Code above omitted 👆
@@ -82,14 +150,27 @@ We can confirm that by printing our heroes before adding them to the database:
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py!}
```
</details>
////
///
That will output:
@@ -120,22 +201,49 @@ After we add the `Hero` instance objects to the **session**, the IDs are *still*
We can verify by creating a session using a `with` block and adding the objects. And then printing them again:
//// tab | Python 3.10+
```Python hl_lines="19-21"
# Code above omitted 👆
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001_py310.py[ln:21-39]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="19-21"
# Code above omitted 👆
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py[ln:23-41]!}
# Code below ommitted 👇
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py!}
```
</details>
////
///
This will, again, output the `id`s of the objects as `None`:
@@ -160,7 +268,21 @@ As we saw before, the **session** is smart and doesn't talk to the database ever
Then we can `commit` the changes in the session, and print again:
```Python hl_lines="13 16-18"
//// tab | Python 3.10+
```Python hl_lines="13 16-18"
# Code above omitted 👆
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001_py310.py[ln:31-46]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="13 16-18"
# Code above omitted 👆
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py[ln:33-48]!}
@@ -168,14 +290,27 @@ Then we can `commit` the changes in the session, and print again:
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py!}
```
</details>
////
///
And now, something unexpected happens, look at the output, it seems as if the `Hero` instance objects had no data at all:
@@ -198,9 +333,9 @@ INFO Engine COMMIT
// And now our prints
After committing the session
Hero 1:
Hero 2:
Hero 3:
Hero 1:
Hero 2:
Hero 3:
// What is happening here? 😱
```
@@ -233,22 +368,49 @@ We didn't access the object's attributes, like `hero.name`. We only accessed the
To confirm and understand how this **automatic expiration and refresh** of data when accessing attributes work, we can print some individual fields (instance attributes):
```Python hl_lines="21-23 26-28"
//// tab | Python 3.10+
```Python hl_lines="21-23 26-28"
# Code above omitted 👆
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001_py310.py[ln:31-56]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="21-23 26-28"
# Code above omitted 👆
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py[ln:33-58]!}
# Code below ommitted 👇
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py!}
```
</details>
////
///
Now we are actually accessing the attributes, because instead of printing the whole object `hero_1`:
@@ -271,21 +433,21 @@ Let's see how it works:
```console
$ python app.py
// Output above ommitted 👆
// Output above omitted 👆
// After committing, the objects are expired and have no values
After committing the session
Hero 1:
Hero 2:
Hero 3:
Hero 1:
Hero 2:
Hero 3:
// Now we will access an attribute like the ID, this is the first print
After committing the session, show IDs
// Notice that before printing the first ID, the Session makes the Engine go to the database to refresh the data 🤓
INFO Engine BEGIN (implicit)
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
FROM hero
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
FROM hero
WHERE hero.id = ?
INFO Engine [generated in 0.00017s] (1,)
@@ -293,8 +455,8 @@ INFO Engine [generated in 0.00017s] (1,)
Hero 1 ID: 1
// Before the next print, refresh the data for the second object
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
FROM hero
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
FROM hero
WHERE hero.id = ?
INFO Engine [cached since 0.001245s ago] (2,)
@@ -302,8 +464,8 @@ INFO Engine [cached since 0.001245s ago] (2,)
Hero 2 ID: 2
// Before the third print, refresh its data
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
FROM hero
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
FROM hero
WHERE hero.id = ?
INFO Engine [cached since 0.002215s ago] (3,)
@@ -317,7 +479,6 @@ Hero 2 name: Spider-Boy
Hero 3 name: Rusty-Man
// Because the Session already refreshed these objects with all their data and the session knows they are not expired, it doesn't have to go again to the database for the names 🤓
```
</div>
@@ -330,22 +491,49 @@ But what if you want to **explicitly refresh** the data?
You can do that too with `session.refresh(object)`:
```Python hl_lines="30-32 35-37"
//// tab | Python 3.10+
```Python hl_lines="30-32 35-37"
# Code above omitted 👆
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001_py310.py[ln:31-65]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="30-32 35-37"
# Code above omitted 👆
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py[ln:33-67]!}
# Code below ommitted 👇
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py!}
```
</details>
////
///
When Python executes this code:
@@ -362,23 +550,23 @@ Here's how the output would look like:
```console
$ python app.py
// Output above ommitted 👆
// Output above omitted 👆
// The first refresh
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
WHERE hero.id = ?
INFO Engine [generated in 0.00024s] (1,)
// The second refresh
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
WHERE hero.id = ?
INFO Engine [cached since 0.001487s ago] (2,)
// The third refresh
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
WHERE hero.id = ?
INFO Engine [cached since 0.002377s ago] (3,)
@@ -403,6 +591,20 @@ Now, as a final experiment, we can also print data after the **session** is clos
There are no surprises here, it still works:
//// tab | Python 3.10+
```Python hl_lines="40-42"
# Code above omitted 👆
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001_py310.py[ln:31-70]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="40-42"
# Code above omitted 👆
@@ -411,14 +613,27 @@ There are no surprises here, it still works:
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial001.py!}
```
</details>
////
///
And the output shows again the same data:
@@ -427,7 +642,7 @@ And the output shows again the same data:
```console
$ python app.py
// Output above ommitted 👆
// Output above omitted 👆
// By finishing the with block, the Session is closed, including a rollback of any pending transaction that could have been there and was not committed
INFO Engine ROLLBACK
@@ -445,17 +660,34 @@ Hero 3: age=48 id=3 name='Rusty-Man' secret_name='Tommy Sharp'
Now let's review all this code once again.
!!! tip
Each one of the numbered bubbles shows what each line will print in the output.
/// tip
And as we created the **engine** with `echo=True`, we can see the SQL statements being executed at each step.
Each one of the numbered bubbles shows what each line will print in the output.
```{ .python .annotate }
And as we created the **engine** with `echo=True`, we can see the SQL statements being executed at each step.
///
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial002_py310.py!}
```
{!./docs_src/tutorial/automatic_id_none_refresh/annotations/en/tutorial002.md!}
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/automatic_id_none_refresh/tutorial002.py!}
```
{!./docs_src/tutorial/automatic_id_none_refresh/annotations/en/tutorial002.md!}
////
And here's all the output generated by running this program, all together:
<div class="termy">
@@ -468,12 +700,12 @@ INFO Engine PRAGMA main.table_info("hero")
INFO Engine [raw sql] ()
INFO Engine PRAGMA temp.table_info("hero")
INFO Engine [raw sql] ()
INFO Engine
INFO Engine
CREATE TABLE hero (
id INTEGER,
name VARCHAR NOT NULL,
secret_name VARCHAR NOT NULL,
age INTEGER,
id INTEGER,
name VARCHAR NOT NULL,
secret_name VARCHAR NOT NULL,
age INTEGER,
PRIMARY KEY (id)
)
@@ -497,23 +729,23 @@ INFO Engine INSERT INTO hero (name, secret_name, age) VALUES (?, ?, ?)
INFO Engine [cached since 0.001483s ago] ('Rusty-Man', 'Tommy Sharp', 48)
INFO Engine COMMIT
After committing the session
Hero 1:
Hero 2:
Hero 3:
Hero 1:
Hero 2:
Hero 3:
After committing the session, show IDs
INFO Engine BEGIN (implicit)
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
FROM hero
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
FROM hero
WHERE hero.id = ?
INFO Engine [generated in 0.00029s] (1,)
Hero 1 ID: 1
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
FROM hero
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
FROM hero
WHERE hero.id = ?
INFO Engine [cached since 0.002132s ago] (2,)
Hero 2 ID: 2
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
FROM hero
INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age
FROM hero
WHERE hero.id = ?
INFO Engine [cached since 0.003367s ago] (3,)
Hero 3 ID: 3
@@ -521,16 +753,16 @@ After committing the session, show names
Hero 1 name: Deadpond
Hero 2 name: Spider-Boy
Hero 3 name: Rusty-Man
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
WHERE hero.id = ?
INFO Engine [generated in 0.00025s] (1,)
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
WHERE hero.id = ?
INFO Engine [cached since 0.001583s ago] (2,)
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
WHERE hero.id = ?
INFO Engine [cached since 0.002722s ago] (3,)
After refreshing the heroes

View File

@@ -138,7 +138,7 @@ So, the output would be:
$ python -m project.app
Created hero: id=1 secret_name='Dive Wilson' team_id=1 name='Deadpond' age=None
Hero's team: name='Z-Force' headquarters='Sister Margarets Bar' id=1
Hero's team: name='Z-Force' headquarters='Sister Margaret's Bar' id=1
```
</div>
@@ -149,10 +149,13 @@ Let's say that for some reason you hate the idea of having all the database mode
You can also do it. 😎 There's a couple of things to keep in mind. 🤓
!!! warning
This is a bit more advanced.
/// warning
If the solution above already worked for you, that might be enough for you, and you can continue in the next chapter. 🤓
This is a bit more advanced.
If the solution above already worked for you, that might be enough for you, and you can continue in the next chapter. 🤓
///
Let's assume that now the file structure is:
@@ -168,7 +171,7 @@ Let's assume that now the file structure is:
### Circular Imports and Type Annotations
The problem with circular imports is that Python can't resolve them at <abbr title="While it is executing the program, as oposed to the code as just text in a file stored on disk.">*runtime*</abbr>.
The problem with circular imports is that Python can't resolve them at <abbr title="While it is executing the program, as opposed to the code as just text in a file stored on disk.">*runtime*</abbr>.
But when using Python **type annotations** it's very common to need to declare the type of some variables with classes imported from other files.
@@ -240,7 +243,7 @@ And running that achieves the same result as before:
$ python -m project.app
Created hero: id=1 age=None name='Deadpond' secret_name='Dive Wilson' team_id=1
Hero's team: id=1 name='Z-Force' headquarters='Sister Margarets Bar'
Hero's team: id=1 name='Z-Force' headquarters='Sister Margaret's Bar'
```
</div>

View File

@@ -12,7 +12,7 @@ The `team` table will look like this:
<td>1</td><td>Preventers</td><td>Sharp Tower</td>
</tr>
<tr>
<td>2</td><td>Z-Force</td><td>Sister Margarets Bar</td>
<td>2</td><td>Z-Force</td><td>Sister Margaret's Bar</td>
</tr>
</table>
@@ -37,19 +37,33 @@ Each row in the table `hero` will point to a row in the table `team`:
<img alt="table relationships" src="/img/tutorial/relationships/select/relationships2.svg">
!!! info
We will later update **Spider-Boy** to add him to the **Preventers** team too, but not yet.
/// info
We will later update **Spider-Boy** to add him to the **Preventers** team too, but not yet.
///
We will continue with the code in the previous example and we will add more things to it.
<details>
<summary>👀 Full file preview</summary>
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/connect/create_tables/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/connect/create_tables/tutorial001.py!}
```
</details>
////
///
Make sure you remove the `database.db` file before running the examples to get the same results.
@@ -61,6 +75,20 @@ And now we will also create the teams there. 🎉
Let's start by creating two teams:
//// tab | Python 3.10+
```Python hl_lines="3-9"
# Code above omitted 👆
{!./docs_src/tutorial/connect/insert/tutorial001_py310.py[ln:29-35]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="3-9"
# Code above omitted 👆
@@ -69,14 +97,27 @@ Let's start by creating two teams:
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/connect/insert/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/connect/insert/tutorial001.py!}
```
</details>
////
///
This would hopefully look already familiar.
@@ -92,6 +133,20 @@ And finally we **commit** the session to save the changes to the database.
Let's not forget to add this function `create_heroes()` to the `main()` function so that we run it when calling the program from the command line:
//// tab | Python 3.10+
```Python hl_lines="5"
# Code above omitted 👆
{!./docs_src/tutorial/connect/insert/tutorial001_py310.py[ln:61-63]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="5"
# Code above omitted 👆
@@ -100,14 +155,27 @@ Let's not forget to add this function `create_heroes()` to the `main()` function
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/connect/insert/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/connect/insert/tutorial001.py!}
```
</details>
////
///
## Run it
@@ -126,7 +194,7 @@ INFO Engine BEGIN (implicit)
INFO Engine INSERT INTO team (name, headquarters) VALUES (?, ?)
INFO Engine [generated in 0.00050s] ('Preventers', 'Sharp Tower')
INFO Engine INSERT INTO team (name, headquarters) VALUES (?, ?)
INFO Engine [cached since 0.002324s ago] ('Z-Force', 'Sister Margarets Bar')
INFO Engine [cached since 0.002324s ago] ('Z-Force', 'Sister Margaret's Bar')
INFO Engine COMMIT
```
@@ -140,6 +208,20 @@ Now let's create one hero object to start.
As the `Hero` class model now has a field (column, attribute) `team_id`, we can set it by using the ID field from the `Team` objects we just created before:
//// tab | Python 3.10+
```Python hl_lines="12"
# Code above omitted 👆
{!./docs_src/tutorial/connect/insert/tutorial001_py310.py[ln:29-39]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="12"
# Code above omitted 👆
@@ -148,18 +230,31 @@ As the `Hero` class model now has a field (column, attribute) `team_id`, we can
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/connect/insert/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/connect/insert/tutorial001.py!}
```
</details>
////
///
We haven't committed this hero to the database yet, but there are already a couple of things to pay **attention** to.
If the database already had some teams, we wouldn't even know **what is the ID** that is going to be automatically assigned to each team by the database, for example, we couldn't just guess `1` or `2`.
If the database already had some teams, we wouldn't even know **what is the ID** that is going to be automatically assigned to each team by the database, for example, we couldn't just guess `1` or `2`.
But once the team is created and committed to the database, we can access the object's `id` field to get that ID.
@@ -171,14 +266,28 @@ That line alone would generate an output of:
```
INFO Engine BEGIN (implicit)
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
FROM team
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
FROM team
WHERE team.id = ?
INFO Engine [generated in 0.00025s] (2,)
```
Let's now create two more heroes:
//// tab | Python 3.10+
```Python hl_lines="14-20"
# Code above omitted 👆
{!./docs_src/tutorial/connect/insert/tutorial001_py310.py[ln:29-50]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="14-20"
# Code above omitted 👆
@@ -187,20 +296,33 @@ Let's now create two more heroes:
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/connect/insert/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/connect/insert/tutorial001.py!}
```
</details>
////
///
When creating `hero_rusty_man`, we are accessing `team_preventers.id`, so that will also trigger a refresh of its data, generating an output of:
```
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
FROM team
INFO Engine SELECT team.id AS team_id, team.name AS team_name, team.headquarters AS team_headquarters
FROM team
WHERE team.id = ?
INFO Engine [cached since 0.001795s ago] (1,)
```
@@ -225,7 +347,21 @@ INFO Engine COMMIT
Now let's refresh and print those new heroes to see their new ID pointing to their teams:
```Python hl_lines="26-28 30-32"
//// tab | Python 3.10+
```Python hl_lines="26-28 30-32"
# Code above omitted 👆
{!./docs_src/tutorial/connect/insert/tutorial001_py310.py[ln:29-58]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="26-28 30-32"
# Code above omitted 👆
{!./docs_src/tutorial/connect/insert/tutorial001.py[ln:31-60]!}
@@ -233,15 +369,27 @@ Now let's refresh and print those new heroes to see their new ID pointing to the
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/connect/insert/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/connect/insert/tutorial001.py!}
```
</details>
////
///
If we execute that in the command line, it will output:
@@ -256,18 +404,18 @@ $ python app.py
INFO Engine BEGIN (implicit)
// Refresh the first hero
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id
FROM hero
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id
FROM hero
WHERE hero.id = ?
INFO Engine [generated in 0.00021s] (1,)
// Refresh the second hero
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id
FROM hero
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id
FROM hero
WHERE hero.id = ?
INFO Engine [cached since 0.001575s ago] (2,)
// Refresh the third hero
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id
FROM hero
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id
FROM hero
WHERE hero.id = ?
INFO Engine [cached since 0.002518s ago] (3,)

View File

@@ -16,7 +16,7 @@ The team table will look like this:
<td>1</td><td>Preventers</td><td>Sharp Tower</td>
</tr>
<tr>
<td>2</td><td>Z-Force</td><td>Sister Margarets Bar</td>
<td>2</td><td>Z-Force</td><td>Sister Margaret's Bar</td>
</tr>
</table>
@@ -57,20 +57,45 @@ Let's start by creating the tables in code.
Import the things we need from `sqlmodel` and create a new `Team` model:
//// tab | Python 3.10+
```Python hl_lines="4-7"
{!./docs_src/tutorial/connect/create_tables/tutorial001_py310.py[ln:1-7]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="6-9"
{!./docs_src/tutorial/connect/create_tables/tutorial001.py[ln:1-9]!}
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/connect/create_tables/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/connect/create_tables/tutorial001.py!}
```
</details>
////
///
This is very similar to what we have been doing with the `Hero` model.
@@ -89,24 +114,49 @@ Now let's create the `hero` table.
This is the same model we have been using up to now, we are just adding the new column `team_id`:
//// tab | Python 3.10+
```Python hl_lines="16"
{!./docs_src/tutorial/connect/create_tables/tutorial001_py310.py[ln:1-16]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="18"
{!./docs_src/tutorial/connect/create_tables/tutorial001.py[ln:1-18]!}
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/connect/create_tables/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/connect/create_tables/tutorial001.py!}
```
</details>
////
///
Most of that should look familiar:
The column will be named `team_id`. It will be an integer, and it could be `NULL` in the database (or `None` in Python), becase there could be some heroes that don't belong to any team.
The column will be named `team_id`. It will be an integer, and it could be `NULL` in the database (or `None` in Python), because there could be some heroes that don't belong to any team.
We add a default of `None` to the `Field()` so we don't have to explicitly pass `team_id=None` when creating a hero.
@@ -126,49 +176,105 @@ This is the name of the **table** in the database, so it is `"team"`, not the na
If you had a custom table name, you would use that custom table name.
!!! info
You can learn about setting a custom table name for a model in the Advanced User Guide.
/// info
You can learn about setting a custom table name for a model in the Advanced User Guide.
///
### Create the Tables
Now we can add the same code as before to create the engine and the function to create the tables:
//// tab | Python 3.10+
```Python hl_lines="3-4 6 9-10"
# Code above omitted 👆
{!./docs_src/tutorial/connect/create_tables/tutorial001_py310.py[ln:19-26]!}
```
////
//// tab | Python 3.7+
```Python hl_lines="3-4 6 9-10"
# Code above omitted 👆
{!./docs_src/tutorial/connect/create_tables/tutorial001.py[ln:21-28]!}
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/connect/create_tables/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/connect/create_tables/tutorial001.py!}
```
</details>
////
///
And as before, we'll call this function from another function `main()`, and we'll add that function `main()` to the main block of the file:
//// tab | Python 3.10+
```Python hl_lines="3-4 7-8"
# Code above omitted 👆
{!./docs_src/tutorial/connect/create_tables/tutorial001_py310.py[ln:29-34]!}
```
////
//// tab | Python 3.7+
```Python hl_lines="3-4 7-8"
# Code above omitted 👆
{!./docs_src/tutorial/connect/create_tables/tutorial001.py[ln:31-36]!}
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/connect/create_tables/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/connect/create_tables/tutorial001.py!}
```
</details>
////
///
## Run the Code
!!! tip
Before running the code, make sure you delete the file `database.db` to make sure you start from scratch.
/// tip
Before running the code, make sure you delete the file `database.db` to make sure you start from scratch.
///
If we run the code we have up to now, it will go and create the database file `database.db` and the tables in it we just defined, `team` and `hero`:
@@ -191,24 +297,24 @@ INFO Engine PRAGMA temp.table_info("hero")
INFO Engine [raw sql] ()
// Create the tables
INFO Engine
INFO Engine
CREATE TABLE team (
id INTEGER,
name VARCHAR NOT NULL,
headquarters VARCHAR NOT NULL,
id INTEGER,
name VARCHAR NOT NULL,
headquarters VARCHAR NOT NULL,
PRIMARY KEY (id)
)
INFO Engine [no key 0.00010s] ()
INFO Engine
INFO Engine
CREATE TABLE hero (
id INTEGER,
name VARCHAR NOT NULL,
secret_name VARCHAR NOT NULL,
age INTEGER,
team_id INTEGER,
PRIMARY KEY (id),
id INTEGER,
name VARCHAR NOT NULL,
secret_name VARCHAR NOT NULL,
age INTEGER,
team_id INTEGER,
PRIMARY KEY (id),
FOREIGN KEY(team_id) REFERENCES team (id)
)
@@ -229,9 +335,9 @@ So, the first SQL could also be written as:
```SQL
CREATE TABLE team (
id INTEGER,
name TEXT NOT NULL,
headquarters TEXT NOT NULL,
id INTEGER,
name TEXT NOT NULL,
headquarters TEXT NOT NULL,
PRIMARY KEY (id)
)
```
@@ -240,12 +346,12 @@ And the second table could be written as:
```SQL hl_lines="8"
CREATE TABLE hero (
id INTEGER,
name TEXT NOT NULL,
secret_name TEXT NOT NULL,
age INTEGER,
team_id INTEGER,
PRIMARY KEY (id),
id INTEGER,
name TEXT NOT NULL,
secret_name TEXT NOT NULL,
age INTEGER,
team_id INTEGER,
PRIMARY KEY (id),
FOREIGN KEY(team_id) REFERENCES team (id)
)
```

View File

@@ -6,7 +6,10 @@ But the main advantage and feature of SQL databases is being able to handle rela
Let's see how to use **SQLModel** to manage connected data in the next chapters. 🤝
!!! tip
We will extend this further in the next group of chapters making it even more convenient to work with in Python code, using **relationship attributes**.
/// tip
But you should start in this group of chapters first. 🤓
We will extend this further in the next group of chapters making it even more convenient to work with in Python code, using **relationship attributes**.
But you should start in this group of chapters first. 🤓
///

View File

@@ -12,7 +12,7 @@ The `team` table has this data:
<td>1</td><td>Preventers</td><td>Sharp Tower</td>
</tr>
<tr>
<td>2</td><td>Z-Force</td><td>Sister Margarets Bar</td>
<td>2</td><td>Z-Force</td><td>Sister Margaret's Bar</td>
</tr>
</table>
@@ -35,14 +35,25 @@ And the `hero` table has this data:
We will continue with the code in the previous example and we will add more things to it.
<details>
<summary>👀 Full file preview</summary>
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/connect/insert/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/connect/insert/tutorial001.py!}
```
</details>
////
///
## `SELECT` Connected Data with SQL
@@ -62,8 +73,11 @@ FROM hero, team
WHERE hero.team_id = team.id
```
!!! info
Because we have two columns called `name`, one for `hero` and one for `team`, we can specify them with the prefix of the table name and the dot to make it explicit what we refer to.
/// info
Because we have two columns called `name`, one for `hero` and one for `team`, we can specify them with the prefix of the table name and the dot to make it explicit what we refer to.
///
Notice that now in the `WHERE` part we are not comparing one column with a literal value (like `hero.name = "Deadpond"`), but we are comparing two columns.
@@ -99,14 +113,17 @@ You can go ahead and try it in **DB Browser for SQLite**:
<img class="shadow" src="/img/tutorial/relationships/select/image01.png">
!!! note
Wait, what about Spider-Boy? 😱
/// note
He doesn't have a team, so his `team_id` is `NULL` in the database. And this SQL is comparing that `NULL` from the `team_id` with all the `id` fields in the rows in the `team` table.
Wait, what about Spider-Boy? 😱
As there's no team with an ID of `NULL`, it doesn't find a match.
He doesn't have a team, so his `team_id` is `NULL` in the database. And this SQL is comparing that `NULL` from the `team_id` with all the `id` fields in the rows in the `team` table.
But we'll see how to fix that later with a `LEFT JOIN`.
As there's no team with an ID of `NULL`, it doesn't find a match.
But we'll see how to fix that later with a `LEFT JOIN`.
///
## Select Related Data with **SQLModel**
@@ -118,6 +135,20 @@ Remember SQLModel's `select()` function? It can take more than one argument.
So, we can pass the `Hero` and `Team` model classes. And we can also use both their columns in the `.where()` part:
//// tab | Python 3.10+
```Python hl_lines="5"
# Code above omitted 👆
{!./docs_src/tutorial/connect/select/tutorial001_py310.py[ln:61-63]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="5"
# Code above omitted 👆
@@ -126,14 +157,27 @@ So, we can pass the `Hero` and `Team` model classes. And we can also use both th
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/connect/select/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/connect/select/tutorial001.py!}
```
</details>
////
///
Notice that in the comparison with `==` we are using the class attributes for both `Hero.team_id` and `Team.id`.
@@ -143,6 +187,20 @@ Now we can execute it and get the `results` object.
And as we used `select` with two models, we will receive tuples of instances of those two models, so we can iterate over them naturally in a `for` loop:
//// tab | Python 3.10+
```Python hl_lines="7"
# Code above omitted 👆
{!./docs_src/tutorial/connect/select/tutorial001_py310.py[ln:61-66]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="7"
# Code above omitted 👆
@@ -151,28 +209,58 @@ And as we used `select` with two models, we will receive tuples of instances of
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/connect/select/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/connect/select/tutorial001.py!}
```
</details>
////
///
For each iteration in the `for` loop we get a a tuple with an instance of the class `Hero` and an instance of the class `Team`.
And in this `for` loop we assign them to the variable `hero` and the variable `team`.
!!! info
There was a lot of research, design, and work behind **SQLModel** to make this provide the best possible developer experience.
/// info
And you should get autocompletion and inline errors in your editor for both `hero` and `team`. 🎉
There was a lot of research, design, and work behind **SQLModel** to make this provide the best possible developer experience.
And you should get autocompletion and inline errors in your editor for both `hero` and `team`. 🎉
///
## Add It to Main
As always, we must remember to add this new `select_heroes()` function to the `main()` function to make sure it is executed when we call this program from the command line.
//// tab | Python 3.10+
```Python hl_lines="6"
# Code above omitted 👆
{!./docs_src/tutorial/connect/select/tutorial001_py310.py[ln:69-72]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="6"
# Code above omitted 👆
@@ -181,14 +269,27 @@ As always, we must remember to add this new `select_heroes()` function to the `m
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/connect/select/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/connect/select/tutorial001.py!}
```
</details>
////
///
## Run the Program
@@ -203,13 +304,13 @@ $ python app.py
// Previous output omitted 😉
// Get the heroes with their teams
2021-08-09 08:55:50,682 INFO sqlalchemy.engine.Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id, team.id AS id_1, team.name AS name_1, team.headquarters
FROM hero, team
2021-08-09 08:55:50,682 INFO sqlalchemy.engine.Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id, team.id AS id_1, team.name AS name_1, team.headquarters
FROM hero, team
WHERE hero.team_id = team.id
2021-08-09 08:55:50,682 INFO sqlalchemy.engine.Engine [no key 0.00015s] ()
// Print the first hero and team
Hero: id=1 secret_name='Dive Wilson' team_id=2 name='Deadpond' age=None Team: headquarters='Sister Margarets Bar' id=2 name='Z-Force'
Hero: id=1 secret_name='Dive Wilson' team_id=2 name='Deadpond' age=None Team: headquarters='Sister Margaret's Bar' id=2 name='Z-Force'
// Print the second hero and team
Hero: id=2 secret_name='Tommy Sharp' team_id=1 name='Rusty-Man' age=48 Team: headquarters='Sharp Tower' id=1 name='Preventers'
@@ -281,10 +382,13 @@ Also in **DB Browser for SQLite**:
<img class="shadow" src="/img/tutorial/relationships/select/image02.png">
!!! tip
Why bother with all this if the result is the same?
/// tip
This `JOIN` will be useful in a bit to be able to also get Spider-Boy, even if he doesn't have a team.
Why bother with all this if the result is the same?
This `JOIN` will be useful in a bit to be able to also get Spider-Boy, even if he doesn't have a team.
///
## Join Tables in **SQLModel**
@@ -292,6 +396,20 @@ The same way there's a `.where()` available when using `select()`, there's also
And in SQLModel (actually SQLAlchemy), when using the `.join()`, because we already declared what is the `foreign_key` when creating the models, we don't have to pass an `ON` part, it is inferred automatically:
//// tab | Python 3.10+
```Python hl_lines="5"
# Code above omitted 👆
{!./docs_src/tutorial/connect/select/tutorial002_py310.py[ln:61-66]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="5"
# Code above omitted 👆
@@ -300,14 +418,27 @@ And in SQLModel (actually SQLAlchemy), when using the `.join()`, because we alre
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/connect/select/tutorial002_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/connect/select/tutorial002.py!}
```
</details>
////
///
Also notice that we are still including `Team` in the `select(Hero, Team)`, because we still want to access that data.
@@ -323,12 +454,12 @@ $ python app.py
// Previous output omitted 😉
// Select using a JOIN with automatic ON
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id, team.id AS id_1, team.name AS name_1, team.headquarters
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id, team.id AS id_1, team.name AS name_1, team.headquarters
FROM hero JOIN team ON team.id = hero.team_id
INFO Engine [no key 0.00032s] ()
// Print the first hero and team
Hero: id=1 secret_name='Dive Wilson' team_id=2 name='Deadpond' age=None Team: headquarters='Sister Margarets Bar' id=2 name='Z-Force'
Hero: id=1 secret_name='Dive Wilson' team_id=2 name='Deadpond' age=None Team: headquarters='Sister Margaret's Bar' id=2 name='Z-Force'
// Print the second hero and team
Hero: id=2 secret_name='Tommy Sharp' team_id=1 name='Rusty-Man' age=48 Team: headquarters='Sharp Tower' id=1 name='Preventers'
@@ -420,8 +551,11 @@ And that would return the following result, including **Spider-Boy** 🎉:
</tr>
</table>
!!! tip
The only difference between this query and the previous is that extra `LEFT OUTER`.
/// tip
The only difference between this query and the previous is that extra `LEFT OUTER`.
///
And here's another of the SQL variations, you could write `LEFT OUTER JOIN` or just `LEFT JOIN`, it means the same.
@@ -431,6 +565,20 @@ Now let's replicate the same query in **SQLModel**.
`.join()` has a parameter we can use `isouter=True` to make the `JOIN` be a `LEFT OUTER JOIN`:
//// tab | Python 3.10+
```Python hl_lines="5"
# Code above omitted 👆
{!./docs_src/tutorial/connect/select/tutorial003_py310.py[ln:61-66]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="5"
# Code above omitted 👆
@@ -439,14 +587,27 @@ Now let's replicate the same query in **SQLModel**.
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/connect/select/tutorial003_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/connect/select/tutorial003.py!}
```
</details>
////
///
And if we run it, it will output:
@@ -458,13 +619,13 @@ $ python app.py
// Previous output omitted 😉
// SELECT using LEFT OUTER JOIN
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id, team.id AS id_1, team.name AS name_1, team.headquarters
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id, team.id AS id_1, team.name AS name_1, team.headquarters
FROM hero LEFT OUTER JOIN team ON team.id = hero.team_id
INFO Engine [no key 0.00051s] ()
// Print the first hero and team
Hero: id=1 secret_name='Dive Wilson' team_id=2 name='Deadpond' age=None Team: headquarters='Sister Margarets Bar' id=2 name='Z-Force'
Hero: id=1 secret_name='Dive Wilson' team_id=2 name='Deadpond' age=None Team: headquarters='Sister Margaret's Bar' id=2 name='Z-Force'
// Print the second hero and team
Hero: id=2 secret_name='Tommy Sharp' team_id=1 name='Rusty-Man' age=48 Team: headquarters='Sharp Tower' id=1 name='Preventers'
// Print the third hero and team, we included Spider-Boy 🎉
@@ -493,6 +654,20 @@ But we would still be able to **filter** the rows with it. 🤓
We could even add some additional `.where()` after `.join()` to filter the data more, for example to return only the heroes from one team:
//// tab | Python 3.10+
```Python hl_lines="5"
# Code above omitted 👆
{!./docs_src/tutorial/connect/select/tutorial004_py310.py[ln:61-66]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="5"
# Code above omitted 👆
@@ -501,14 +676,27 @@ We could even add some additional `.where()` after `.join()` to filter the data
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/connect/select/tutorial004_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/connect/select/tutorial004.py!}
```
</details>
////
///
Here we are **filtering** with `.where()` to get only the heroes that belong to the **Preventers** team.
@@ -522,9 +710,9 @@ If we run that, it would output:
$ python app.py
// Select only the hero data
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id
// But still join with the team table
FROM hero JOIN team ON team.id = hero.team_id
FROM hero JOIN team ON team.id = hero.team_id
// And filter with WHERE to get only the Preventers
WHERE team.name = ?
INFO Engine [no key 0.00066s] ('Preventers',)
@@ -539,6 +727,20 @@ Preventer Hero: id=2 secret_name='Tommy Sharp' team_id=1 name='Rusty-Man' age=48
By putting the `Team` in `select()` we tell **SQLModel** and the database that we want the team data too.
//// tab | Python 3.10+
```Python hl_lines="5"
# Code above omitted 👆
{!./docs_src/tutorial/connect/select/tutorial005_py310.py[ln:61-66]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="5"
# Code above omitted 👆
@@ -547,14 +749,27 @@ By putting the `Team` in `select()` we tell **SQLModel** and the database that w
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/connect/select/tutorial005_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/connect/select/tutorial005.py!}
```
</details>
////
///
And if we run that, it will output:
@@ -564,9 +779,9 @@ And if we run that, it will output:
$ python app.py
// Select the hero and the team data
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id, team.id AS id_1, team.name AS name_1, team.headquarters
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id, team.id AS id_1, team.name AS name_1, team.headquarters
// Join the hero with the team table
FROM hero JOIN team ON team.id = hero.team_id
FROM hero JOIN team ON team.id = hero.team_id
// Filter with WHERE to get only Preventers
WHERE team.name = ?
INFO Engine [no key 0.00018s] ('Preventers',)

View File

@@ -10,7 +10,7 @@ We currently have a `team` table:
<td>1</td><td>Preventers</td><td>Sharp Tower</td>
</tr>
<tr>
<td>2</td><td>Z-Force</td><td>Sister Margarets Bar</td>
<td>2</td><td>Z-Force</td><td>Sister Margaret's Bar</td>
</tr>
</table>
@@ -35,14 +35,25 @@ Let's see how to **remove** connections between rows in tables.
We will continue with the code from the previous chapter.
<details>
<summary>👀 Full file preview</summary>
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/connect/update/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/connect/update/tutorial001.py!}
```
</details>
////
///
## Break a Connection
@@ -52,11 +63,29 @@ Let's say **Spider-Boy** is tired of the lack of friendly neighbors and wants to
We can simply set the `team_id` to `None`, and now it doesn't have a connection with the team:
//// tab | Python 3.10+
```Python hl_lines="8"
# Code above omitted 👆
{!./docs_src/tutorial/connect/delete/tutorial001_py310.py[ln:29-30]!}
# Previous code here omitted 👈
{!./docs_src/tutorial/connect/delete/tutorial001_py310.py[ln:66-70]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="8"
# Code above omitted 👆
{!./docs_src/tutorial/connect/delete/tutorial001.py[ln:31-32]!}
# Previous code here omitted 👈
{!./docs_src/tutorial/connect/delete/tutorial001.py[ln:68-72]!}
@@ -64,14 +93,27 @@ We can simply set the `team_id` to `None`, and now it doesn't have a connection
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/connect/delete/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/connect/delete/tutorial001.py!}
```
</details>
////
///
Again, we just **assign** a value to that field attribute `team_id`, now the value is `None`, which means `NULL` in the database. Then we `add()` the hero to the session, and then `commit()`.
@@ -94,8 +136,8 @@ INFO Engine COMMIT
// Automatically start a new transaction
INFO Engine BEGIN (implicit)
// Refresh the hero
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id
FROM hero
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id
FROM hero
WHERE hero.id = ?
INFO Engine [cached since 0.1661s ago] (3,)

View File

@@ -10,7 +10,7 @@ At this point we have a `team` table:
<td>1</td><td>Preventers</td><td>Sharp Tower</td>
</tr>
<tr>
<td>2</td><td>Z-Force</td><td>Sister Margarets Bar</td>
<td>2</td><td>Z-Force</td><td>Sister Margaret's Bar</td>
</tr>
</table>
@@ -37,14 +37,25 @@ Now we'll see how to **update** those connections between rows tables.
We will continue with the code we used to create some heroes, and we'll update them.
<details>
<summary>👀 Full file preview</summary>
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/connect/insert/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/connect/insert/tutorial001.py!}
```
</details>
////
///
## Assign a Team to a Hero
@@ -52,11 +63,29 @@ Let's say that **Tommy Sharp** uses his "rich uncle" charms to recruit **Spider-
Doing it is just like updating any other field:
//// tab | Python 3.10+
```Python hl_lines="8"
# Code above omitted 👆
{!./docs_src/tutorial/connect/update/tutorial001_py310.py[ln:29-30]!}
# Previous code here omitted 👈
{!./docs_src/tutorial/connect/update/tutorial001_py310.py[ln:60-64]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="8"
# Code above omitted 👆
{!./docs_src/tutorial/connect/update/tutorial001.py[ln:31-32]!}
# Previous code here omitted 👈
{!./docs_src/tutorial/connect/update/tutorial001.py[ln:62-66]!}
@@ -64,14 +93,27 @@ Doing it is just like updating any other field:
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/connect/update/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/connect/update/tutorial001.py!}
```
</details>
////
///
We can simply **assign** a value to that field attribute `team_id`, then `add()` the hero to the session, and then `commit()`.
@@ -94,8 +136,8 @@ INFO Engine COMMIT
// Automatically start a new transaction
INFO Engine BEGIN (implicit)
// Refresh the hero data
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id
FROM hero
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age, hero.team_id
FROM hero
WHERE hero.id = ?
INFO Engine [cached since 0.08837s ago] (3,)

View File

@@ -42,8 +42,11 @@ Click the button <kbd>New Database</kbd>.
A dialog should show up. Go to the [project directory you created](./index.md#create-a-project){.internal-link target=_blank} and save the file with a name of `database.db`.
!!! tip
It's common to save SQLite database files with an extension of `.db`. Sometimes also `.sqlite`.
/// tip
It's common to save SQLite database files with an extension of `.db`. Sometimes also `.sqlite`.
///
## Create a Table
@@ -122,6 +125,8 @@ And delete that `./database.db` file in your project directory.
And click again on <kbd>New Database</kbd>.
Save the file with the name `database.db` again.
This time, if you see the dialog to create a new table, just close it by clicking the <kbd>Cancel</kbd> button.
And now, go to the tab <kbd>Execute SQL</kbd>.
@@ -164,6 +169,6 @@ Of course, you can also go and take a full SQL course or read a book about SQL,
We saw how to interact with SQLite databases in files using **DB Browser for SQLite** in a visual user interface.
We also saw how to use it to write some SQL directly to the SQLite database. This will be useful to verify the data in the database is looking correclty, to debug, etc.
We also saw how to use it to write some SQL directly to the SQLite database. This will be useful to verify the data in the database is looking correctly, to debug, etc.
In the next chapters we will start using **SQLModel** to interact with the database, and we will continue to use **DB Browser for SQLite** at the same time to look at the database underneath. 🔍

View File

@@ -33,34 +33,65 @@ The first thing we need to do is create a class to represent the data in the tab
A class like this that represents some data is commonly called a **model**.
!!! tip
That's why this package is called `SQLModel`. Because it's mainly used to create **SQL Models**.
/// tip
That's why this package is called `SQLModel`. Because it's mainly used to create **SQL Models**.
///
For that, we will import `SQLModel` (plus other things we will also use) and create a class `Hero` that inherits from `SQLModel` and represents the **table model** for our heroes:
//// tab | Python 3.10+
```Python hl_lines="1 4"
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py[ln:1-8]!}
# More code here later 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="3 6"
{!./docs_src/tutorial/create_db_and_table/tutorial001.py[ln:1-10]!}
# More code here later 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/create_db_and_table/tutorial001.py!}
```
</details>
////
///
This class `Hero` **represents the table** for our heroes. And each instance we create later will **represent a row** in the table.
We use the config `table=True` to tell **SQLModel** that this is a **table model**, it represents a table.
!!! info
It's also possible to have models without `table=True`, those would be only **data models**, without a table in the database, they would not be **table models**.
/// info
Those **data models** will be **very useful later**, but for now, we'll just keep adding the `table=True` configuration.
It's also possible to have models without `table=True`, those would be only **data models**, without a table in the database, they would not be **table models**.
Those **data models** will be **very useful later**, but for now, we'll just keep adding the `table=True` configuration.
///
## Define the Fields, Columns
@@ -70,26 +101,51 @@ The name of each of these variables will be the name of the column in the table.
And the type of each of them will also be the type of table column:
//// tab | Python 3.10+
```Python hl_lines="1 5-8"
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py[ln:1-8]!}
# More code here later 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="1 3 7-10"
{!./docs_src/tutorial/create_db_and_table/tutorial001.py[ln:1-10]!}
# More code here later 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/create_db_and_table/tutorial001.py!}
```
</details>
////
///
Let's now see with more detail these field/column declarations.
### Optional Fields, Nullable Columns
Let's start with `age`, notice that it has a type of `Optional[int]`.
Let's start with `age`, notice that it has a type of `int | None (or Optional[int])`.
And we import that `Optional` from the `typing` standard module.
@@ -97,23 +153,51 @@ That is the standard way to declare that something "could be an `int` or `None`"
And we also set the default value of `age` to `None`.
//// tab | Python 3.10+
```Python hl_lines="8"
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py[ln:1-8]!}
# More code here later 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="1 10"
{!./docs_src/tutorial/create_db_and_table/tutorial001.py[ln:1-10]!}
# More code here later 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/create_db_and_table/tutorial001.py!}
```
</details>
////
!!! tip
We also define `id` with `Optional`. But we will talk about `id` below.
///
/// tip
We also define `id` with `Optional`. But we will talk about `id` below.
///
This way, we tell **SQLModel** that `age` is not required when validating data and that it has a default value of `None`.
@@ -121,10 +205,13 @@ And we also tell it that, in the SQL database, the default value of `age` is `NU
So, this column is "nullable" (can be set to `NULL`).
!!! info
In terms of **Pydantic**, `age` is an **optional field**.
/// info
In terms of **SQLAlchemy**, `age` is a **nullable column**.
In terms of **Pydantic**, `age` is an **optional field**.
In terms of **SQLAlchemy**, `age` is a **nullable column**.
///
### Primary Key `id`
@@ -134,20 +221,45 @@ So, we need to mark `id` as the **primary key**.
To do that, we use the special `Field` function from `sqlmodel` and set the argument `primary_key=True`:
//// tab | Python 3.10+
```Python hl_lines="1 5"
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py[ln:1-8]!}
# More code here later 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="3 7"
{!./docs_src/tutorial/create_db_and_table/tutorial001.py[ln:1-10]!}
# More code here later 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/create_db_and_table/tutorial001.py!}
```
</details>
////
///
That way, we tell **SQLModel** that this `id` field/column is the primary key of the table.
@@ -190,54 +302,107 @@ If you have a server database (for example PostgreSQL or MySQL), the **engine**
Creating the **engine** is very simple, just call `create_engine()` with a URL for the database to use:
```Python hl_lines="3 16"
{!./docs_src/tutorial/create_db_and_table/tutorial001.py[ln:1-16]!}
//// tab | Python 3.10+
```Python hl_lines="1 14"
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py[ln:1-16]!}
# More code here later 👇
```
<details>
<summary>👀 Full file preview</summary>
////
//// tab | Python 3.7+
```Python hl_lines="3 16"
{!./docs_src/tutorial/create_db_and_table/tutorial001.py[ln:1-18]!}
# More code here later 👇
```
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/create_db_and_table/tutorial001.py!}
```
</details>
////
///
You should normally have a single **engine** object for your whole application and re-use it everywhere.
!!! tip
There's another related thing called a **Session** that normally should *not* be a single object per application.
/// tip
But we will talk about it later.
There's another related thing called a **Session** that normally should *not* be a single object per application.
But we will talk about it later.
///
### Engine Database URL
Each supported database has it's own URL type. For example, for **SQLite** it is `sqlite:///` followed by the file path. For example:
Each supported database has its own URL type. For example, for **SQLite** it is `sqlite:///` followed by the file path. For example:
* `sqlite:///database.db`
* `sqlite:///databases/local/application.db`
* `sqlite:///db.sqlite`
For SQLAlchemy, there's also a special one, which is a database all *in memory*, this means that it is deleted after the program terminates, and it's also very fast:
SQLite supports a special database that lives all *in memory*. Hence, it's very fast, but be careful, the database gets deleted after the program terminates. You can specify this in-memory database by using just two slash characters (`//`) and no file name:
* `sqlite://`
```Python hl_lines="13-14 16"
{!./docs_src/tutorial/create_db_and_table/tutorial001.py[ln:1-19]!}
//// tab | Python 3.10+
```Python hl_lines="11-12 14"
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py[ln:1-16]!}
# More code here later 👇
```
<details>
<summary>👀 Full file preview</summary>
////
//// tab | Python 3.7+
```Python hl_lines="13-14 16"
{!./docs_src/tutorial/create_db_and_table/tutorial001.py[ln:1-18]!}
# More code here later 👇
```
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/create_db_and_table/tutorial001.py!}
```
</details>
////
///
You can read a lot more about all the databases supported by **SQLAlchemy** (and that way supported by **SQLModel**) in the <a href="https://docs.sqlalchemy.org/en/14/core/engines.html" class="external-link" target="_blank">SQLAlchemy documentation</a>.
@@ -249,20 +414,45 @@ It will make the engine print all the SQL statements it executes, which can help
It is particularly useful for **learning** and **debugging**:
```Python hl_lines="16"
{!./docs_src/tutorial/create_db_and_table/tutorial001.py[ln:1-16]!}
//// tab | Python 3.10+
```Python hl_lines="14"
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py[ln:1-16]!}
# More code here later 👇
```
<details>
<summary>👀 Full file preview</summary>
////
//// tab | Python 3.7+
```Python hl_lines="16"
{!./docs_src/tutorial/create_db_and_table/tutorial001.py[ln:1-18]!}
# More code here later 👇
```
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/create_db_and_table/tutorial001.py!}
```
</details>
////
///
But in production, you would probably want to remove `echo=True`:
@@ -272,12 +462,15 @@ engine = create_engine(sqlite_url)
### Engine Technical Details
!!! tip
If you didn't know about SQLAlchemy before and are just learning **SQLModel**, you can probably skip this section, scroll below.
/// tip
If you didn't know about SQLAlchemy before and are just learning **SQLModel**, you can probably skip this section, scroll below.
///
You can read a lot more about the engine in the <a href="https://docs.sqlalchemy.org/en/14/tutorial/engine.html" class="external-link" target="_blank">SQLAlchemy documentation</a>.
**SQLModel** defines it's own `create_engine()` function. It is the same as SQLAlchemy's `create_engine()`, but with the difference that it defaults to use `future=True` (which means that it uses the style of the latest SQLAlchemy, 1.4, and the future 2.0).
**SQLModel** defines its own `create_engine()` function. It is the same as SQLAlchemy's `create_engine()`, but with the difference that it defaults to use `future=True` (which means that it uses the style of the latest SQLAlchemy, 1.4, and the future 2.0).
And SQLModel's version of `create_engine()` is type annotated internally, so your editor will be able to help you with autocompletion and inline errors.
@@ -285,16 +478,31 @@ And SQLModel's version of `create_engine()` is type annotated internally, so you
Now everything is in place to finally create the database and table:
//// tab | Python 3.10+
```Python hl_lines="16"
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python hl_lines="18"
{!./docs_src/tutorial/create_db_and_table/tutorial001.py!}
```
!!! tip
Creating the engine doesn't create the `database.db` file.
////
But once we run `SQLModel.metadata.create_all(engine)`, it creates the `database.db` file **and** creates the `hero` table in that database.
/// tip
Both things are done in this single step.
Creating the engine doesn't create the `database.db` file.
But once we run `SQLModel.metadata.create_all(engine)`, it creates the `database.db` file **and** creates the `hero` table in that database.
Both things are done in this single step.
///
Let's unwrap that:
@@ -395,17 +603,31 @@ Let's run the program to see it all working.
Put the code it in a file `app.py` if you haven't already.
<details>
<summary>👀 Full file preview</summary>
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/create_db_and_table/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/create_db_and_table/tutorial001.py!}
```
</details>
////
!!! tip
Remember to [activate the virtual environment](./index.md#create-a-python-virtual-environment){.internal-link target=_blank} before running it.
///
/// tip
Remember to [activate the virtual environment](./index.md#create-a-python-virtual-environment){.internal-link target=_blank} before running it.
///
Now run the program with Python:
@@ -415,22 +637,22 @@ Now run the program with Python:
// We set echo=True, so this will show the SQL code
$ python app.py
// First, some boilerplate SQL that we are not that intereted in
// First, some boilerplate SQL that we are not that interested in
INFO Engine BEGIN (implicit)
INFO Engine PRAGMA main.table_info("hero")
INFO Engine [raw sql] ()
INFO Engine PRAGMA temp.table_info("hero")
INFO Engine [raw sql] ()
INFO Engine
INFO Engine
// Finally, the glorious SQL to create the table ✨
CREATE TABLE hero (
id INTEGER,
name VARCHAR NOT NULL,
secret_name VARCHAR NOT NULL,
age INTEGER,
id INTEGER,
name VARCHAR NOT NULL,
secret_name VARCHAR NOT NULL,
age INTEGER,
PRIMARY KEY (id)
)
@@ -442,20 +664,23 @@ INFO Engine COMMIT
</div>
!!! info
I simplified the output above a bit to make it easier to read.
/// info
But in reality, instead of showing:
I simplified the output above a bit to make it easier to read.
```
INFO Engine BEGIN (implicit)
```
But in reality, instead of showing:
it would show something like:
```
INFO Engine BEGIN (implicit)
```
```
2021-07-25 21:37:39,175 INFO sqlalchemy.engine.Engine BEGIN (implicit)
```
it would show something like:
```
2021-07-25 21:37:39,175 INFO sqlalchemy.engine.Engine BEGIN (implicit)
```
///
### `TEXT` or `VARCHAR`
@@ -463,7 +688,7 @@ In the example in the previous chapter we created the table using `TEXT` for som
But in this output SQLAlchemy is using `VARCHAR` instead. Let's see what's going on.
Remember that [each SQL Database has some different variations in what they support?](../databases/#sql-the-language){.internal-link target=_blank}
Remember that [each SQL Database has some different variations in what they support?](../databases.md#sql-the-language){.internal-link target=_blank}
This is one of the differences. Each database supports some particular **data types**, like `INTEGER` and `TEXT`.
@@ -479,8 +704,11 @@ Additional to the difference between those two data types, some databases like M
To make it easier to start using **SQLModel** right away independent of the database you use (even with MySQL), and without any extra configurations, by default, `str` fields are interpreted as `VARCHAR` in most databases and `VARCHAR(255)` in MySQL, this way you know the same class will be compatible with the most popular databases without extra effort.
!!! tip
You will learn how to change the maximum length of string columns later in the Advanced Tutorial - User Guide.
/// tip
You will learn how to change the maximum length of string columns later in the Advanced Tutorial - User Guide.
///
### Verify the Database
@@ -498,20 +726,45 @@ In this example it's just the `SQLModel.metadata.create_all(engine)`.
Let's put it in a function `create_db_and_tables()`:
//// tab | Python 3.10+
```Python hl_lines="17-18"
{!./docs_src/tutorial/create_db_and_table/tutorial002_py310.py[ln:1-18]!}
# More code here later 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="19-20"
{!./docs_src/tutorial/create_db_and_table/tutorial002.py[ln:1-20]!}
# More code here later 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/create_db_and_table/tutorial002_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/create_db_and_table/tutorial002.py!}
```
</details>
////
///
If `SQLModel.metadata.create_all(engine)` was not in a function and we tried to import something from this module (from this file) in another, it would try to create the database and table **every time** we executed that other file that imported this module.
@@ -519,8 +772,11 @@ We don't want that to happen like that, only when we **intend** it to happen, th
Now we would be able to, for example, import the `Hero` class in some other file without having those **side effects**.
!!! tip
😅 **Spoiler alert**: The function is called `create_db_and_tables()` because we will have more **tables** in the future with other classes apart from `Hero`. 🚀
/// tip
😅 **Spoiler alert**: The function is called `create_db_and_tables()` because we will have more **tables** in the future with other classes apart from `Hero`. 🚀
///
### Create Data as a Script
@@ -528,17 +784,32 @@ We prevented the side effects when importing something from your `app.py` file.
But we still want it to **create the database and table** when we call it with Python directly as an independent script from the terminal, just as as above.
!!! tip
Think of the word **script** and **program** as interchangeable.
/// tip
The word **script** often implies that the code could be run independently and easily. Or in some cases it refers to a relatively simple program.
Think of the word **script** and **program** as interchangeable.
The word **script** often implies that the code could be run independently and easily. Or in some cases it refers to a relatively simple program.
///
For that we can use the special variable `__name__` in an `if` block:
//// tab | Python 3.10+
```Python hl_lines="21-22"
{!./docs_src/tutorial/create_db_and_table/tutorial002_py310.py!}
```
////
//// tab | Python 3.7+
```Python hl_lines="23-24"
{!./docs_src/tutorial/create_db_and_table/tutorial002.py!}
```
////
### About `__name__ == "__main__"`
The main purpose of the `__name__ == "__main__"` is to have some code that is executed when your file is called with:
@@ -559,10 +830,13 @@ $ python app.py
from app import Hero
```
!!! tip
That `if` block using `if __name__ == "__main__":` is sometimes called the "**main block**".
/// tip
The official name (in the <a href="https://docs.python.org/3/library/__main__.html" class="external-link" target="_blank">Python docs</a>) is "**Top-level script environment**".
That `if` block using `if __name__ == "__main__":` is sometimes called the "**main block**".
The official name (in the <a href="https://docs.python.org/3/library/__main__.html" class="external-link" target="_blank">Python docs</a>) is "**Top-level script environment**".
///
#### More details
@@ -614,8 +888,11 @@ if __name__ == "__main__":
...will **not** be executed.
!!! info
For more information, check <a href="https://docs.python.org/3/library/__main__.html" class="external-link" target="_blank">the official Python docs</a>.
/// info
For more information, check <a href="https://docs.python.org/3/library/__main__.html" class="external-link" target="_blank">the official Python docs</a>.
///
## Last Review
@@ -625,14 +902,31 @@ But now we can import things from this module in other files.
Now, let's give the code a final look:
//// tab | Python 3.10+
```{.python .annotate}
{!./docs_src/tutorial/create_db_and_table/tutorial003_py310.py!}
```
{!./docs_src/tutorial/create_db_and_table/annotations/en/tutorial003.md!}
////
//// tab | Python 3.7+
```{.python .annotate}
{!./docs_src/tutorial/create_db_and_table/tutorial003.py!}
```
{!./docs_src/tutorial/create_db_and_table/annotations/en/tutorial003.md!}
!!! tip
Review what each line does by clicking each number bubble in the code. 👆
////
/// tip
Review what each line does by clicking each number bubble in the code. 👆
///
## Recap

View File

@@ -6,14 +6,25 @@ Now let's delete some data using **SQLModel**.
As before, we'll continue from where we left off with the previous code.
<details>
<summary>👀 Full file preview</summary>
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/update/tutorial003_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/update/tutorial003.py!}
```
</details>
////
///
Remember to remove the `database.db` file before running the examples to get the same results.
@@ -63,6 +74,20 @@ To get the same results, delete the `database.db` file before running the exampl
We'll start by selecting the hero `"Spider-Youngster"` that we updated in the previous chapter, this is the one we will delete:
//// tab | Python 3.10+
```Python hl_lines="5"
# Code above omitted 👆
{!./docs_src/tutorial/delete/tutorial001_py310.py[ln:70-75]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="5"
# Code above omitted 👆
@@ -71,31 +96,69 @@ We'll start by selecting the hero `"Spider-Youngster"` that we updated in the pr
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/delete/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/delete/tutorial001.py!}
```
</details>
////
///
As this is a new function `delete_heroes()`, we'll also add it to the `main()` function so that we call it when executing the program from the command line:
//// tab | Python 3.10+
```Python hl_lines="7"
# Code above omitted 👆
{!./docs_src/tutorial/delete/tutorial001_py310.py[ln:90-98]!}
```
////
//// tab | Python 3.7+
```Python hl_lines="7"
# Code above omitted 👆
{!./docs_src/tutorial/delete/tutorial001.py[ln:92-100]!}
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/delete/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/delete/tutorial001.py!}
```
</details>
////
///
That will print the same existing hero **Spider-Youngster**:
@@ -108,8 +171,8 @@ $ python app.py
// The SELECT with WHERE
INFO Engine BEGIN (implicit)
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
WHERE hero.name = ?
INFO Engine [no key 0.00011s] ('Spider-Youngster',)
@@ -123,6 +186,20 @@ Hero: name='Spider-Youngster' secret_name='Pedro Parqueador' age=16 id=2
Now, very similar to how we used `session.add()` to add or update new heroes, we can use `session.delete()` to delete the hero from the session:
//// tab | Python 3.10+
```Python hl_lines="10"
# Code above omitted 👆
{!./docs_src/tutorial/delete/tutorial001_py310.py[ln:70-77]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="10"
# Code above omitted 👆
@@ -131,14 +208,27 @@ Now, very similar to how we used `session.add()` to add or update new heroes, we
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/delete/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/delete/tutorial001.py!}
```
</details>
////
///
## Commit the Session
@@ -146,6 +236,20 @@ To save the current changes in the session, **commit** it.
This will save all the changes stored in the **session**, like the deleted hero:
//// tab | Python 3.10+
```Python hl_lines="11"
# Code above omitted 👆
{!./docs_src/tutorial/delete/tutorial001_py310.py[ln:70-78]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="11"
# Code above omitted 👆
@@ -154,14 +258,27 @@ This will save all the changes stored in the **session**, like the deleted hero:
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/delete/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/delete/tutorial001.py!}
```
</details>
////
///
The same as we have seen before, `.commit()` will also save anything else that was added to the session. Including updates, or created heroes.
@@ -196,6 +313,20 @@ As the object is not connected to the session, it is not marked as "expired", th
Because of that, the object still contains its attributes with the data in it, so we can print it:
//// tab | Python 3.10+
```Python hl_lines="13"
# Code above omitted 👆
{!./docs_src/tutorial/delete/tutorial001_py310.py[ln:70-80]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="13"
# Code above omitted 👆
@@ -204,14 +335,27 @@ Because of that, the object still contains its attributes with the data in it, s
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/delete/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/delete/tutorial001.py!}
```
</details>
////
///
This will output:
@@ -234,6 +378,20 @@ Deleted hero: name='Spider-Youngster' secret_name='Pedro Parqueador' age=16 id=2
To confirm if it was deleted, now let's query the database again, with the same `"Spider-Youngster"` name:
//// tab | Python 3.10+
```Python hl_lines="15-17"
# Code above omitted 👆
{!./docs_src/tutorial/delete/tutorial001_py310.py[ln:70-84]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="15-17"
# Code above omitted 👆
@@ -242,14 +400,27 @@ To confirm if it was deleted, now let's query the database again, with the same
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/delete/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/delete/tutorial001.py!}
```
</details>
////
///
Here we are using `results.first()` to get the first object found (in case it found multiple) or `None`, if it didn't find anything.
@@ -272,8 +443,8 @@ $ python app.py
INFO Engine BEGIN (implicit)
// SQL to search for the hero
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
WHERE hero.name = ?
INFO Engine [no key 0.00013s] ('Spider-Youngster',)
```
@@ -286,6 +457,20 @@ Now let's just confirm that, indeed, no hero was found in the database with that
We'll do it by checking that the "first" item in the `results` is `None`:
//// tab | Python 3.10+
```Python hl_lines="19-20"
# Code above omitted 👆
{!./docs_src/tutorial/delete/tutorial001_py310.py[ln:70-87]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="19-20"
# Code above omitted 👆
@@ -294,14 +479,27 @@ We'll do it by checking that the "first" item in the `results` is `None`:
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/delete/tutorial001_py310.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/delete/tutorial001.py!}
```
</details>
////
///
This will output:
@@ -327,14 +525,31 @@ INFO Engine ROLLBACK
Now let's review all that code:
//// tab | Python 3.10+
```{ .python .annotate hl_lines="70-88" }
{!./docs_src/tutorial/delete/tutorial002_py310.py!}
```
{!./docs_src/tutorial/delete/annotations/en/tutorial002.md!}
////
//// tab | Python 3.7+
```{ .python .annotate hl_lines="72-90" }
{!./docs_src/tutorial/delete/tutorial002.py!}
```
{!./docs_src/tutorial/delete/annotations/en/tutorial002.md!}
!!! tip
Check out the number bubbles to see what is done by each line of code.
////
/// tip
Check out the number bubbles to see what is done by each line of code.
///
## Recap

View File

@@ -12,6 +12,32 @@ We get a `hero_id` from the path parameter and verify if it exists, just as we d
And if we actually find a hero, we just delete it with the **session**.
//// tab | Python 3.10+
```Python hl_lines="3-11"
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/delete/tutorial001_py310.py[ln:89-97]!}
# Code below omitted 👇
```
////
//// tab | Python 3.9+
```Python hl_lines="3-11"
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/delete/tutorial001_py39.py[ln:91-99]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="3-11"
# Code above omitted 👆
@@ -20,14 +46,35 @@ And if we actually find a hero, we just delete it with the **session**.
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/fastapi/delete/tutorial001_py310.py!}
```
////
//// tab | Python 3.9+
```Python
{!./docs_src/tutorial/fastapi/delete/tutorial001_py39.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/fastapi/delete/tutorial001.py!}
```
</details>
////
///
After deleting it successfully, we just return a response of:

View File

@@ -8,8 +8,11 @@ So, we probably want to limit it.
Let's use the same **offset** and **limit** we learned about in the previous tutorial chapters for the API.
!!! info
In many cases, this is also called **pagination**.
/// info
In many cases, this is also called **pagination**.
///
## Add a Limit and Offset to the Query Parameters
@@ -19,6 +22,36 @@ By default, we will return the first results from the database, so `offset` will
And by default, we will return a maximum of `100` heroes, so `limit` will have a default value of `100`.
//// tab | Python 3.10+
```Python hl_lines="1 7 9"
{!./docs_src/tutorial/fastapi/limit_and_offset/tutorial001_py310.py[ln:1-2]!}
# Code here omitted 👈
{!./docs_src/tutorial/fastapi/limit_and_offset/tutorial001_py310.py[ln:52-56]!}
# Code below omitted 👇
```
////
//// tab | Python 3.9+
```Python hl_lines="3 9 11"
{!./docs_src/tutorial/fastapi/limit_and_offset/tutorial001_py39.py[ln:1-4]!}
# Code here omitted 👈
{!./docs_src/tutorial/fastapi/limit_and_offset/tutorial001_py39.py[ln:54-58]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="3 9 11"
{!./docs_src/tutorial/fastapi/limit_and_offset/tutorial001.py[ln:1-4]!}
@@ -29,29 +62,53 @@ And by default, we will return a maximum of `100` heroes, so `limit` will have a
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/fastapi/limit_and_offset/tutorial001_py310.py!}
```
////
//// tab | Python 3.9+
```Python
{!./docs_src/tutorial/fastapi/limit_and_offset/tutorial001_py39.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/fastapi/limit_and_offset/tutorial001.py!}
```
</details>
////
///
We want to allow clients to set different `offset` and `limit` values.
But we don't want them to be able to set a `limit` of something like `9999`, that's over `9000`! 😱
So, to prevent it, we add additional validation to the `limit` query parameter, declaring that it has to be **l**ess **t**han or **e**qual to `100` with `lte=100`.
So, to prevent it, we add additional validation to the `limit` query parameter, declaring that it has to be **l**ess than or **e**qual to `100` with `le=100`.
This way, a client can decide to take fewer heroes if they want, but not more.
!!! info
If you need to refresh how query parameters and their validation work, check out the docs in FastAPI:
/// info
* <a href="https://fastapi.tiangolo.com/tutorial/query-params/" class="external-link" target="_blank">Query Parameters</a>
* <a href="https://fastapi.tiangolo.com/tutorial/query-params-str-validations/" class="external-link" target="_blank">Query Parameters and String Validations</a>
* <a href="https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/" class="external-link" target="_blank">Path Parameters and Numeric Validations</a>
If you need to refresh how query parameters and their validation work, check out the docs in FastAPI:
* <a href="https://fastapi.tiangolo.com/tutorial/query-params/" class="external-link" target="_blank">Query Parameters</a>
* <a href="https://fastapi.tiangolo.com/tutorial/query-params-str-validations/" class="external-link" target="_blank">Query Parameters and String Validations</a>
* <a href="https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/" class="external-link" target="_blank">Path Parameters and Numeric Validations</a>
///
## Check the Docs UI

View File

@@ -53,11 +53,11 @@ Here's the weird thing, the `id` currently seems also "optional". 🤔
This is because in our **SQLModel** class we declare the `id` with `Optional[int]`, because it could be `None` in memory until we save it in the database and we finally get the actual ID.
But in the responses, we would always send a model from the database, and it would **always have an ID**. So the `id` in the responses could be declared as required too.
But in the responses, we always send a model from the database, so it **always has an ID**. So the `id` in the responses can be declared as required.
This would mean that our application is making the compromise with the clients that if it sends a hero, it would for sure have an `id` with a value, it would not be `None`.
This means that our application is making the promise to the clients that if it sends a hero, it will for sure have an `id` with a value, it will not be `None`.
### Why Is it Important to Compromise with the Responses
### Why Is it Important to Have a Contract for Responses
The ultimate goal of an API is for some **clients to use it**.
@@ -98,7 +98,7 @@ But we also want to have a `HeroCreate` for the data we want to receive when **c
* `secret_name`, required
* `age`, optional
And we want to have a `HeroRead` with the `id` field, but this time annotated with `id: int`, instead of `id: Optional[int]`, to make it clear that it is required in responses **read** from the clients:
And we want to have a `HeroPublic` with the `id` field, but this time annotated with `id: int`, instead of `id: Optional[int]`, to make it clear that it is required in responses **read** from the clients:
* `id`, required
* `name`, required
@@ -109,6 +109,36 @@ And we want to have a `HeroRead` with the `id` field, but this time annotated wi
The simplest way to solve it could be to create **multiple models**, each one with all the corresponding fields:
//// tab | Python 3.10+
```Python hl_lines="5-9 12-15 18-22"
# This would work, but there's a better option below 🚨
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/multiple_models/tutorial001_py310.py[ln:5-22]!}
# Code below omitted 👇
```
////
//// tab | Python 3.9+
```Python hl_lines="5-9 12-15 18-22"
# This would work, but there's a better option below 🚨
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/multiple_models/tutorial001_py39.py[ln:7-24]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="5-9 12-15 18-22"
# This would work, but there's a better option below 🚨
@@ -119,25 +149,49 @@ The simplest way to solve it could be to create **multiple models**, each one wi
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/fastapi/multiple_models/tutorial001_py310.py!}
```
////
//// tab | Python 3.9+
```Python
{!./docs_src/tutorial/fastapi/multiple_models/tutorial001_py39.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/fastapi/multiple_models/tutorial001.py!}
```
</details>
////
///
Here's the important detail, and probably the most important feature of **SQLModel**: only `Hero` is declared with `table = True`.
This means that the class `Hero` represents a **table** in the database. It is both a **Pydantic** model and a **SQLAlchemy** model.
But `HeroCreate` and `HeroRead` don't have `table = True`. They are only **data models**, they are only **Pydantic** models. They won't be used with the database, but only to declare data schemas for the API (or for other uses).
But `HeroCreate` and `HeroPublic` don't have `table = True`. They are only **data models**, they are only **Pydantic** models. They won't be used with the database, but only to declare data schemas for the API (or for other uses).
This also means that `SQLModel.metadata.create_all()` won't create tables in the database for `HeroCreate` and `HeroRead`, because they don't have `table = True`, which is exactly what we want. 🚀
This also means that `SQLModel.metadata.create_all()` won't create tables in the database for `HeroCreate` and `HeroPublic`, because they don't have `table = True`, which is exactly what we want. 🚀
!!! tip
We will improve this code to avoid duplicating the fields, but for now we can continue learning with these models.
/// tip
We will improve this code to avoid duplicating the fields, but for now we can continue learning with these models.
///
## Use Multiple Models to Create a Hero
@@ -145,6 +199,32 @@ Let's now see how to use these new models in the FastAPI application.
Let's first check how is the process to create a hero now:
//// tab | Python 3.10+
```Python hl_lines="3-4 6"
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/multiple_models/tutorial001_py310.py[ln:44-51]!}
# Code below omitted 👇
```
////
//// tab | Python 3.9+
```Python hl_lines="3-4 6"
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/multiple_models/tutorial001_py39.py[ln:46-53]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="3-4 6"
# Code above omitted 👆
@@ -153,19 +233,66 @@ Let's first check how is the process to create a hero now:
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/fastapi/multiple_models/tutorial001_py310.py!}
```
////
//// tab | Python 3.9+
```Python
{!./docs_src/tutorial/fastapi/multiple_models/tutorial001_py39.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/fastapi/multiple_models/tutorial001.py!}
```
</details>
////
///
Let's check that in detail.
Now we use the type annotation `HeroCreate` for the request JSON data in the `hero` parameter of the **path operation function**.
//// tab | Python 3.10+
```Python hl_lines="3"
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/multiple_models/tutorial001_py310.py[ln:45]!}
# Code below omitted 👇
```
////
//// tab | Python 3.9+
```Python hl_lines="3"
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/multiple_models/tutorial001_py39.py[ln:47]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="3"
# Code above omitted 👆
@@ -174,15 +301,45 @@ Now we use the type annotation `HeroCreate` for the request JSON data in the `he
# Code below omitted 👇
```
Then we create a new `Hero` (this is the actual **table** model that saves things to the database) using `Hero.from_orm()`.
////
The method `.from_orm()` reads data from another object with attributes and creates a new instance of this class, in this case `Hero`.
Then we create a new `Hero` (this is the actual **table** model that saves things to the database) using `Hero.model_validate()`.
The alternative is `Hero.parse_obj()` that reads data from a dictionary.
The method `.model_validate()` reads data from another object with attributes (or a dict) and creates a new instance of this class, in this case `Hero`.
But as in this case, we have a `HeroCreate` instance in the `hero` variable. This is an object with attributes, so we use `.from_orm()` to read those attributes.
In this case, we have a `HeroCreate` instance in the `hero` variable. This is an object with attributes, so we use `.model_validate()` to read those attributes.
With this, we create a new `Hero` instance (the one for the database) and put it in the variable `db_hero` from the data in the `hero` variable that is the `HeroCreate` instance we received from the request.
/// tip
In versions of **SQLModel** before `0.0.14` you would use the method `.from_orm()`, but it is now deprecated and you should use `.model_validate()` instead.
///
We can now create a new `Hero` instance (the one for the database) and put it in the variable `db_hero` from the data in the `hero` variable that is the `HeroCreate` instance we received from the request.
//// tab | Python 3.10+
```Python hl_lines="3"
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/multiple_models/tutorial001_py310.py[ln:47]!}
# Code below omitted 👇
```
////
//// tab | Python 3.9+
```Python hl_lines="3"
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/multiple_models/tutorial001_py39.py[ln:49]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="3"
# Code above omitted 👆
@@ -192,11 +349,39 @@ With this, we create a new `Hero` instance (the one for the database) and put it
# Code below omitted 👇
```
////
Then we just `add` it to the **session**, `commit`, and `refresh` it, and finally, we return the same `db_hero` variable that has the just refreshed `Hero` instance.
Because it is just refreshed, it has the `id` field set with a new ID taken from the database.
And now that we return it, FastAPI will validate the data with the `response_model`, which is a `HeroRead`:
And now that we return it, FastAPI will validate the data with the `response_model`, which is a `HeroPublic`:
//// tab | Python 3.10+
```Python hl_lines="3"
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/multiple_models/tutorial001_py310.py[ln:44]!}
# Code below omitted 👇
```
////
//// tab | Python 3.9+
```Python hl_lines="3"
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/multiple_models/tutorial001_py39.py[ln:46]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="3"
# Code above omitted 👆
@@ -206,12 +391,17 @@ And now that we return it, FastAPI will validate the data with the `response_mod
# Code below omitted 👇
```
////
This will validate that all the data that we promised is there and will remove any data we didn't declare.
!!! tip
This filtering could be very important and could be a very good security feature, for example, to make sure you filter private data, hashed passwords, etc.
/// tip
You can read more about it in the <a href="https://fastapi.tiangolo.com/tutorial/response-model/" class="external-link" target="_blank">FastAPI docs about Response Model</a>.
This filtering could be very important and could be a very good security feature, for example, to make sure you filter private data, hashed passwords, etc.
You can read more about it in the <a href="https://fastapi.tiangolo.com/tutorial/response-model/" class="external-link" target="_blank">FastAPI docs about Response Model</a>.
///
In particular, it will make sure that the `id` is there and that it is indeed an integer (and not `None`).
@@ -253,6 +443,32 @@ We can see from above that they all share some **base** fields:
So let's create a **base** model `HeroBase` that the others can inherit from:
//// tab | Python 3.10+
```Python hl_lines="3-6"
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/multiple_models/tutorial002_py310.py[ln:5-8]!}
# Code below omitted 👇
```
////
//// tab | Python 3.9+
```Python hl_lines="3-6"
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/multiple_models/tutorial002_py39.py[ln:7-10]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="3-6"
# Code above omitted 👆
@@ -261,14 +477,35 @@ So let's create a **base** model `HeroBase` that the others can inherit from:
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/fastapi/multiple_models/tutorial002_py310.py!}
```
////
//// tab | Python 3.9+
```Python
{!./docs_src/tutorial/fastapi/multiple_models/tutorial002_py39.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/fastapi/multiple_models/tutorial002.py!}
```
</details>
////
///
As you can see, this is *not* a **table model**, it doesn't have the `table = True` config.
@@ -278,6 +515,32 @@ But now we can create the **other models inheriting from it**, they will all sha
Let's start with the only **table model**, the `Hero`:
//// tab | Python 3.10+
```Python hl_lines="9-10"
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/multiple_models/tutorial002_py310.py[ln:5-12]!}
# Code below omitted 👇
```
////
//// tab | Python 3.9+
```Python hl_lines="9-10"
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/multiple_models/tutorial002_py39.py[ln:7-14]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="9-10"
# Code above omitted 👆
@@ -286,14 +549,35 @@ Let's start with the only **table model**, the `Hero`:
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/fastapi/multiple_models/tutorial002_py310.py!}
```
////
//// tab | Python 3.9+
```Python
{!./docs_src/tutorial/fastapi/multiple_models/tutorial002_py39.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/fastapi/multiple_models/tutorial002.py!}
```
</details>
////
///
Notice that `Hero` now doesn't inherit from `SQLModel`, but from `HeroBase`.
@@ -309,6 +593,32 @@ And those inherited fields will also be in the **autocompletion** and **inline e
Notice that the parent model `HeroBase` is not a **table model**, but still, we can declare `name` and `age` using `Field(index=True)`.
//// tab | Python 3.10+
```Python hl_lines="4 6 9"
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/multiple_models/tutorial002_py310.py[ln:5-12]!}
# Code below omitted 👇
```
////
//// tab | Python 3.9+
```Python hl_lines="4 6 9"
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/multiple_models/tutorial002_py39.py[ln:7-14]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="4 6 9"
# Code above omitted 👆
@@ -317,14 +627,35 @@ Notice that the parent model `HeroBase` is not a **table model**, but still, we
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/fastapi/multiple_models/tutorial002_py310.py!}
```
////
//// tab | Python 3.9+
```Python
{!./docs_src/tutorial/fastapi/multiple_models/tutorial002_py39.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/fastapi/multiple_models/tutorial002.py!}
```
</details>
////
///
This won't affect this parent **data model** `HeroBase`.
@@ -336,6 +667,32 @@ Now let's see the `HeroCreate` model that will be used to define the data that w
This is a fun one:
//// tab | Python 3.10+
```Python hl_lines="13-14"
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/multiple_models/tutorial002_py310.py[ln:5-16]!}
# Code below omitted 👇
```
////
//// tab | Python 3.9+
```Python hl_lines="13-14"
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/multiple_models/tutorial002_py39.py[ln:7-18]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="13-14"
# Code above omitted 👆
@@ -344,14 +701,35 @@ This is a fun one:
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/fastapi/multiple_models/tutorial002_py310.py!}
```
////
//// tab | Python 3.9+
```Python
{!./docs_src/tutorial/fastapi/multiple_models/tutorial002_py39.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/fastapi/multiple_models/tutorial002.py!}
```
</details>
////
///
What's happening here?
@@ -365,12 +743,38 @@ As an alternative, we could use `HeroBase` directly in the API code instead of `
On top of that, we could easily decide in the future that we want to receive **more data** when creating a new hero apart from the data in `HeroBase` (for example, a password), and now we already have the class to put those extra fields.
### The `HeroRead` **Data Model**
### The `HeroPublic` **Data Model**
Now let's check the `HeroRead` model.
Now let's check the `HeroPublic` model.
This one just declares that the `id` field is required when reading a hero from the API, because a hero read from the API will come from the database, and in the database it will always have an ID.
//// tab | Python 3.10+
```Python hl_lines="17-18"
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/multiple_models/tutorial002_py310.py[ln:5-20]!}
# Code below omitted 👇
```
////
//// tab | Python 3.9+
```Python hl_lines="17-18"
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/multiple_models/tutorial002_py39.py[ln:7-22]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="17-18"
# Code above omitted 👆
@@ -379,18 +783,39 @@ This one just declares that the `id` field is required when reading a hero from
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/fastapi/multiple_models/tutorial002_py310.py!}
```
////
//// tab | Python 3.9+
```Python
{!./docs_src/tutorial/fastapi/multiple_models/tutorial002_py39.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/fastapi/multiple_models/tutorial002.py!}
```
</details>
////
///
## Review the Updated Docs UI
The FastAPI code is still the same as above, we still use `Hero`, `HeroCreate`, and `HeroRead`. But now, we define them in a smarter way with inheritance.
The FastAPI code is still the same as above, we still use `Hero`, `HeroCreate`, and `HeroPublic`. But now, we define them in a smarter way with inheritance.
So, we can jump to the docs UI right away and see how they look with the updated data.

View File

@@ -8,8 +8,37 @@ Let's add a new *path operation* to read one single hero.
We want to get the hero based on the `id`, so we will use a **path parameter** `hero_id`.
!!! info
If you need to refresh how *path parameters* work, including their data validation, check the <a href="https://fastapi.tiangolo.com/tutorial/path-params/" class="external-link" target="_blank">FastAPI docs about Path Parameters</a>.
/// info
If you need to refresh how *path parameters* work, including their data validation, check the <a href="https://fastapi.tiangolo.com/tutorial/path-params/" class="external-link" target="_blank">FastAPI docs about Path Parameters</a>.
///
//// tab | Python 3.10+
```Python hl_lines="6"
{!./docs_src/tutorial/fastapi/read_one/tutorial001_py310.py[ln:1-2]!}
# Code here omitted 👈
{!./docs_src/tutorial/fastapi/read_one/tutorial001_py310.py[ln:59-65]!}
```
////
//// tab | Python 3.9+
```Python hl_lines="8"
{!./docs_src/tutorial/fastapi/read_one/tutorial001_py39.py[ln:1-4]!}
# Code here omitted 👈
{!./docs_src/tutorial/fastapi/read_one/tutorial001_py39.py[ln:61-67]!}
```
////
//// tab | Python 3.7+
```Python hl_lines="8"
{!./docs_src/tutorial/fastapi/read_one/tutorial001.py[ln:1-4]!}
@@ -19,14 +48,35 @@ We want to get the hero based on the `id`, so we will use a **path parameter** `
{!./docs_src/tutorial/fastapi/read_one/tutorial001.py[ln:61-67]!}
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/fastapi/read_one/tutorial001_py310.py!}
```
////
//// tab | Python 3.9+
```Python
{!./docs_src/tutorial/fastapi/read_one/tutorial001_py39.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/fastapi/read_one/tutorial001.py!}
```
</details>
////
///
For example, to get the hero with ID `2` we would send a `GET` request to:
@@ -46,6 +96,32 @@ And to use it, we first import `HTTPException` from `fastapi`.
This will let the client know that they probably made a mistake on their side and requested a hero that doesn't exist in the database.
//// tab | Python 3.10+
```Python hl_lines="1 9-11"
{!./docs_src/tutorial/fastapi/read_one/tutorial001_py310.py[ln:1-2]!}
# Code here omitted 👈
{!./docs_src/tutorial/fastapi/read_one/tutorial001_py310.py[ln:59-65]!}
```
////
//// tab | Python 3.9+
```Python hl_lines="3 11-13"
{!./docs_src/tutorial/fastapi/read_one/tutorial001_py39.py[ln:1-4]!}
# Code here omitted 👈
{!./docs_src/tutorial/fastapi/read_one/tutorial001_py39.py[ln:61-67]!}
```
////
//// tab | Python 3.7+
```Python hl_lines="3 11-13"
{!./docs_src/tutorial/fastapi/read_one/tutorial001.py[ln:1-4]!}
@@ -54,20 +130,67 @@ This will let the client know that they probably made a mistake on their side an
{!./docs_src/tutorial/fastapi/read_one/tutorial001.py[ln:61-67]!}
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/fastapi/read_one/tutorial001_py310.py!}
```
////
//// tab | Python 3.9+
```Python
{!./docs_src/tutorial/fastapi/read_one/tutorial001_py39.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/fastapi/read_one/tutorial001.py!}
```
</details>
////
///
## Return the Hero
Then, if the hero exists, we return it.
And because we are using the `response_model` with `HeroRead`, it will be validated, documented, etc.
And because we are using the `response_model` with `HeroPublic`, it will be validated, documented, etc.
//// tab | Python 3.10+
```Python hl_lines="6 12"
{!./docs_src/tutorial/fastapi/read_one/tutorial001_py310.py[ln:1-2]!}
# Code here omitted 👈
{!./docs_src/tutorial/fastapi/read_one/tutorial001_py310.py[ln:59-65]!}
```
////
//// tab | Python 3.9+
```Python hl_lines="8 14"
{!./docs_src/tutorial/fastapi/read_one/tutorial001_py39.py[ln:1-4]!}
# Code here omitted 👈
{!./docs_src/tutorial/fastapi/read_one/tutorial001_py39.py[ln:61-67]!}
```
////
//// tab | Python 3.7+
```Python hl_lines="8 14"
{!./docs_src/tutorial/fastapi/read_one/tutorial001.py[ln:1-4]!}
@@ -77,14 +200,35 @@ And because we are using the `response_model` with `HeroRead`, it will be valida
{!./docs_src/tutorial/fastapi/read_one/tutorial001.py[ln:61-67]!}
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/fastapi/read_one/tutorial001_py310.py!}
```
////
//// tab | Python 3.9+
```Python
{!./docs_src/tutorial/fastapi/read_one/tutorial001_py39.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/fastapi/read_one/tutorial001.py!}
```
</details>
////
///
## Check the Docs UI

View File

@@ -40,9 +40,59 @@ Let's update that. 🤓
First, why is it that we are not getting the related data for each hero and for each team?
It's because we declared the `HeroRead` with only the same base fields of the `HeroBase` plus the `id`. But it doesn't include a field `team` for the **relationship attribute**.
It's because we declared the `HeroPublic` with only the same base fields of the `HeroBase` plus the `id`. But it doesn't include a field `team` for the **relationship attribute**.
And the same way, we declared the `TeamRead` with only the same base fields of the `TeamBase` plus the `id`. But it doesn't include a field `heroes` for the **relationship attribute**.
And the same way, we declared the `TeamPublic` with only the same base fields of the `TeamBase` plus the `id`. But it doesn't include a field `heroes` for the **relationship attribute**.
//// tab | Python 3.10+
```Python hl_lines="3-5 9-10 14-19 23-24"
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/teams/tutorial001_py310.py[ln:5-7]!}
# Code here omitted 👈
{!./docs_src/tutorial/fastapi/teams/tutorial001_py310.py[ln:20-21]!}
# Code here omitted 👈
{!./docs_src/tutorial/fastapi/teams/tutorial001_py310.py[ln:29-34]!}
# Code here omitted 👈
{!./docs_src/tutorial/fastapi/teams/tutorial001_py310.py[ln:43-44]!}
# Code below omitted 👇
```
////
//// tab | Python 3.9+
```Python hl_lines="3-5 9-10 14-19 23-24"
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/teams/tutorial001_py39.py[ln:7-9]!}
# Code here omitted 👈
{!./docs_src/tutorial/fastapi/teams/tutorial001_py39.py[ln:22-23]!}
# Code here omitted 👈
{!./docs_src/tutorial/fastapi/teams/tutorial001_py39.py[ln:31-36]!}
# Code here omitted 👈
{!./docs_src/tutorial/fastapi/teams/tutorial001_py39.py[ln:45-46]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="3-5 9-10 14-19 23-24"
# Code above omitted 👆
@@ -64,18 +114,73 @@ And the same way, we declared the `TeamRead` with only the same base fields of t
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/fastapi/teams/tutorial001_py310.py!}
```
////
//// tab | Python 3.9+
```Python
{!./docs_src/tutorial/fastapi/teams/tutorial001_py39.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/fastapi/teams/tutorial001.py!}
```
</details>
////
///
Now, remember that <a href="https://fastapi.tiangolo.com/tutorial/response-model/" class="external-link" target="_blank">FastAPI uses the `response_model` to validate and **filter** the response data</a>?
In this case, we used `response_model=TeamRead` and `response_model=HeroRead`, so FastAPI will use them to filter the response data, even if we return a **table model** that includes **relationship attributes**:
In this case, we used `response_model=TeamPublic` and `response_model=HeroPublic`, so FastAPI will use them to filter the response data, even if we return a **table model** that includes **relationship attributes**:
//// tab | Python 3.10+
```Python hl_lines="3 8 12 17"
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/teams/tutorial001_py310.py[ln:102-107]!}
# Code here omitted 👈
{!./docs_src/tutorial/fastapi/teams/tutorial001_py310.py[ln:156-161]!}
# Code below omitted 👇
```
////
//// tab | Python 3.9+
```Python hl_lines="3 8 12 17"
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/teams/tutorial001_py39.py[ln:104-109]!}
# Code here omitted 👈
{!./docs_src/tutorial/fastapi/teams/tutorial001_py39.py[ln:158-163]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="3 8 12 17"
# Code above omitted 👆
@@ -84,19 +189,40 @@ In this case, we used `response_model=TeamRead` and `response_model=HeroRead`, s
# Code here omitted 👈
{!./docs_src/tutorial/fastapi/teams/tutorial001.py[ln:159-164]!}
{!./docs_src/tutorial/fastapi/teams/tutorial001.py[ln:158-163]!}
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/fastapi/teams/tutorial001_py310.py!}
```
////
//// tab | Python 3.9+
```Python
{!./docs_src/tutorial/fastapi/teams/tutorial001_py39.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/fastapi/teams/tutorial001.py!}
```
</details>
////
///
## Don't Include All the Data
@@ -174,10 +300,36 @@ Let's add a couple more **data models** that declare that data so we can use the
## Models with Relationships
Let's add the models `HeroReadWithTeam` and `TeamReadWithHeroes`.
Let's add the models `HeroPublicWithTeam` and `TeamPublicWithHeroes`.
We'll add them **after** the other models so that we can easily reference the previous models.
//// tab | Python 3.10+
```Python hl_lines="3-4 7-8"
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/relationships/tutorial001_py310.py[ln:59-64]!}
# Code below omitted 👇
```
////
//// tab | Python 3.9+
```Python hl_lines="3-4 7-8"
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/relationships/tutorial001_py39.py[ln:61-66]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="3-4 7-8"
# Code above omitted 👆
@@ -186,24 +338,45 @@ We'll add them **after** the other models so that we can easily reference the pr
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/fastapi/relationships/tutorial001_py310.py!}
```
////
//// tab | Python 3.9+
```Python
{!./docs_src/tutorial/fastapi/relationships/tutorial001_py39.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/fastapi/relationships/tutorial001.py!}
```
</details>
////
///
These two models are very **simple in code**, but there's a lot happening here. Let's check it out.
### Inheritance and Type Annotations
The `HeroReadWithTeam` **inherits** from `HeroRead`, which means that it will have the **normal fields for reading**, including the required `id` that was declared in `HeroRead`.
The `HeroPublicWithTeam` **inherits** from `HeroPublic`, which means that it will have the **normal fields for reading**, including the required `id` that was declared in `HeroPublic`.
And then it adds the **new field** `team`, which could be `None`, and is declared with the type `TeamRead` with the base fields for reading a team.
And then it adds the **new field** `team`, which could be `None`, and is declared with the type `TeamPublic` with the base fields for reading a team.
Then we do the same for the `TeamReadWithHeroes`, it **inherits** from `TeamRead`, and declares the **new field** `heroes`, which is a list of `HeroRead`.
Then we do the same for the `TeamPublicWithHeroes`, it **inherits** from `TeamPublic`, and declares the **new field** `heroes`, which is a list of `HeroPublic`.
### Data Models Without Relationship Attributes
@@ -213,11 +386,11 @@ Instead, here these are only **data models** that will tell FastAPI **which attr
### Reference to Other Models
Also, notice that the field `team` is not declared with this new `TeamReadWithHeroes`, because that would again create that infinite recursion of data. Instead, we declare it with the normal `TeamRead` model.
Also, notice that the field `team` is not declared with this new `TeamPublicWithHeroes`, because that would again create that infinite recursion of data. Instead, we declare it with the normal `TeamPublic` model.
And the same for `TeamReadWithHeroes`, the model used for the new field `heroes` uses `HeroRead` to get only each hero's data.
And the same for `TeamPublicWithHeroes`, the model used for the new field `heroes` uses `HeroPublic` to get only each hero's data.
This also means that, even though we have these two new models, **we still need the previous ones**, `HeroRead` and `TeamRead`, because we need to reference them here (and we are also using them in the rest of the *path operations*).
This also means that, even though we have these two new models, **we still need the previous ones**, `HeroPublic` and `TeamPublic`, because we need to reference them here (and we are also using them in the rest of the *path operations*).
## Update the Path Operations
@@ -227,6 +400,40 @@ This will tell **FastAPI** to take the object that we return from the *path oper
In the case of the hero, this tells FastAPI to extract the `team` too. And in the case of the team, to extract the list of `heroes` too.
//// tab | Python 3.10+
```Python hl_lines="3 8 12 17"
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/relationships/tutorial001_py310.py[ln:111-116]!}
# Code here omitted 👈
{!./docs_src/tutorial/fastapi/relationships/tutorial001_py310.py[ln:165-170]!}
# Code below omitted 👇
```
////
//// tab | Python 3.9+
```Python hl_lines="3 8 12 17"
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/relationships/tutorial001_py39.py[ln:113-118]!}
# Code here omitted 👈
{!./docs_src/tutorial/fastapi/relationships/tutorial001_py39.py[ln:167-172]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="3 8 12 17"
# Code above omitted 👆
@@ -234,19 +441,40 @@ In the case of the hero, this tells FastAPI to extract the `team` too. And in th
# Code here omitted 👈
{!./docs_src/tutorial/fastapi/relationships/tutorial001.py[ln:168-173]!}
{!./docs_src/tutorial/fastapi/relationships/tutorial001.py[ln:167-172]!}
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/fastapi/relationships/tutorial001_py310.py!}
```
////
//// tab | Python 3.9+
```Python
{!./docs_src/tutorial/fastapi/relationships/tutorial001_py39.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/fastapi/relationships/tutorial001.py!}
```
</details>
////
///
## Check It Out in the Docs UI
@@ -267,7 +495,7 @@ Now we get the **team** data included:
"id": 1,
"team": {
"name": "Z-Force",
"headquarters": "Sister Margarets Bar",
"headquarters": "Sister Margaret's Bar",
"id": 1
}
}

View File

@@ -32,6 +32,32 @@ We can use `response_model` to tell FastAPI the schema of the data we want to se
For example, we can pass the same `Hero` **SQLModel** class (because it is also a Pydantic model):
//// tab | Python 3.10+
```Python hl_lines="3"
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/response_model/tutorial001_py310.py[ln:31-37]!}
# Code below omitted 👇
```
////
//// tab | Python 3.9+
```Python hl_lines="3"
# Code above omitted 👆
{!./docs_src/tutorial/fastapi/response_model/tutorial001_py39.py[ln:33-39]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="3"
# Code above omitted 👆
@@ -40,14 +66,35 @@ For example, we can pass the same `Hero` **SQLModel** class (because it is also
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/fastapi/response_model/tutorial001_py310.py!}
```
////
//// tab | Python 3.9+
```Python
{!./docs_src/tutorial/fastapi/response_model/tutorial001_py39.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/fastapi/response_model/tutorial001.py!}
```
</details>
////
///
## List of Heroes in `response_model`
@@ -55,6 +102,34 @@ We can also use other type annotations, the same way we can use with Pydantic fi
First, we import `List` from `typing` and then we declare the `response_model` with `List[Hero]`:
//// tab | Python 3.10+
```Python hl_lines="3"
# Code here omitted 👈
{!./docs_src/tutorial/fastapi/response_model/tutorial001_py310.py[ln:40-44]!}
# Code below omitted 👇
```
////
//// tab | Python 3.9+
```Python hl_lines="3"
# Code here omitted 👈
{!./docs_src/tutorial/fastapi/response_model/tutorial001_py39.py[ln:42-46]!}
# Code below omitted 👇
```
////
//// tab | Python 3.7+
```Python hl_lines="1 5"
{!./docs_src/tutorial/fastapi/response_model/tutorial001.py[ln:1]!}
@@ -65,14 +140,35 @@ First, we import `List` from `typing` and then we declare the `response_model` w
# Code below omitted 👇
```
<details>
<summary>👀 Full file preview</summary>
////
/// details | 👀 Full file preview
//// tab | Python 3.10+
```Python
{!./docs_src/tutorial/fastapi/response_model/tutorial001_py310.py!}
```
////
//// tab | Python 3.9+
```Python
{!./docs_src/tutorial/fastapi/response_model/tutorial001_py39.py!}
```
////
//// tab | Python 3.7+
```Python
{!./docs_src/tutorial/fastapi/response_model/tutorial001.py!}
```
</details>
////
///
## FastAPI and Response Model
@@ -100,10 +196,13 @@ Additionally, because the schemas are defined in using a standard, there are man
For example, client generators, that can automatically create the code necessary to talk to your API in many languages.
!!! info
If you are curious about the standards, FastAPI generates OpenAPI, that internally uses JSON Schema.
/// info
You can read about all that in the <a href="https://fastapi.tiangolo.com/tutorial/first-steps/#openapi" class="external-link" target="_blank">FastAPI docs - First Steps</a>.
If you are curious about the standards, FastAPI generates OpenAPI, that internally uses JSON Schema.
You can read about all that in the <a href="https://fastapi.tiangolo.com/tutorial/first-steps/#openapi" class="external-link" target="_blank">FastAPI docs - First Steps</a>.
///
## Recap

Some files were not shown because too many files have changed in this diff Show More