Skip to content

Commit e915864

Browse files
authored
Merge branch 'main' into main
2 parents 19805e9 + 2fab481 commit e915864

File tree

142 files changed

+2475
-379
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

142 files changed

+2475
-379
lines changed

.github/dependabot.yml

+6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
version: 2
22
updates:
3+
# GitHub Actions
4+
- package-ecosystem: "github-actions"
5+
directory: "/"
6+
schedule:
7+
interval: "daily"
8+
# Python
39
- package-ecosystem: "pip"
410
directory: "/"
511
schedule:

.github/workflows/build-docs.yml

+10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
name: Build Docs
22
on:
33
push:
4+
branches:
5+
- main
46
pull_request:
57
types: [opened, synchronize]
68
workflow_dispatch:
@@ -51,8 +53,16 @@ jobs:
5153
- name: Install Material for MkDocs Insiders
5254
if: github.event.pull_request.head.repo.fork == false && steps.cache.outputs.cache-hit != 'true'
5355
run: python -m poetry run pip install git+https://${{ secrets.ACTIONS_TOKEN }}@github.com/squidfunk/mkdocs-material-insiders.git
56+
- uses: actions/cache@v2
57+
with:
58+
key: mkdocs-cards-${{ github.ref }}
59+
path: .cache
5460
- name: Build Docs
61+
if: github.event.pull_request.head.repo.fork == true
5562
run: python -m poetry run mkdocs build
63+
- name: Build Docs with Insiders
64+
if: github.event.pull_request.head.repo.fork == false
65+
run: python -m poetry run mkdocs build --config-file mkdocs.insiders.yml
5666
- name: Zip docs
5767
run: python -m poetry run bash ./scripts/zip-docs.sh
5868
- uses: actions/upload-artifact@v2

.github/workflows/test.yml

+5-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ name: Test
22

33
on:
44
push:
5+
branches:
6+
- main
57
pull_request:
68
types: [opened, synchronize]
79
workflow_dispatch:
@@ -16,7 +18,7 @@ jobs:
1618
runs-on: ubuntu-20.04
1719
strategy:
1820
matrix:
19-
python-version: [3.6, 3.7, 3.8, 3.9]
21+
python-version: ["3.6.15", "3.7", "3.8", "3.9", "3.10"]
2022
fail-fast: false
2123

2224
steps:
@@ -52,9 +54,9 @@ jobs:
5254
if: steps.cache.outputs.cache-hit != 'true'
5355
run: python -m poetry install
5456
- name: Lint
55-
if: ${{ matrix.python-version != '3.6' }}
57+
if: ${{ matrix.python-version != '3.6.15' }}
5658
run: python -m poetry run bash scripts/lint.sh
5759
- name: Test
5860
run: python -m poetry run bash scripts/test.sh
5961
- name: Upload coverage
60-
uses: codecov/codecov-action@v1
62+
uses: codecov/codecov-action@v2

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ htmlcov
1111
coverage.xml
1212
site
1313
*.db
14+
.cache

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -212,4 +212,4 @@ And at the same time, ✨ it is also a **Pydantic** model ✨. You can use inher
212212

213213
## License
214214

215-
This project is licensed under the terms of the MIT license.
215+
This project is licensed under the terms of the [MIT license](https://github.com/tiangolo/sqlmodel/blob/main/LICENSE).

docs/advanced/decimal.md

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
# Decimal Numbers
2+
3+
In some cases you might need to be able to store decimal numbers with guarantees about the precision.
4+
5+
This is particularly important if you are storing things like **currencies**, **prices**, **accounts**, and others, as you would want to know that you wouldn't have rounding errors.
6+
7+
As an example, if you open Python and sum `1.1` + `2.2` you would expect to see `3.3`, but you will actually get `3.3000000000000003`:
8+
9+
```Python
10+
>>> 1.1 + 2.2
11+
3.3000000000000003
12+
```
13+
14+
This is because of the way numbers are stored in "ones and zeros" (binary). But Python has a module and some types to have strict decimal values. You can read more about it in the official <a href="https://docs.python.org/3/library/decimal.html" class="external-link" target="_blank">Python docs for Decimal</a>.
15+
16+
Because databases store data in the same ways as computers (in binary), they would have the same types of issues. And because of that, they also have a special **decimal** type.
17+
18+
In most cases this would probably not be a problem, for example measuring views in a video, or the life bar in a videogame. But as you can imagine, this is particularly important when dealing with **money** and **finances**.
19+
20+
## Decimal Types
21+
22+
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>.
23+
24+
!!! tip
25+
Pydantic 1.9, that will be released soon, has improved support for `Decimal` types, without needing to use the `condecimal()` function.
26+
27+
But meanwhile, you can already use this feature with `condecimal()` in **SQLModel** it as it's explained here.
28+
29+
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.
30+
31+
!!! info
32+
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>.
33+
34+
## Decimals in SQLModel
35+
36+
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:
37+
38+
```{.python .annotate hl_lines="12" }
39+
{!./docs_src/advanced/decimal/tutorial001.py[ln:1-12]!}
40+
41+
# More code here later 👇
42+
```
43+
44+
<details>
45+
<summary>👀 Full file preview</summary>
46+
47+
```Python
48+
{!./docs_src/advanced/decimal/tutorial001.py!}
49+
```
50+
51+
</details>
52+
53+
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).
54+
55+
We are also saying that the number of decimal places (to the right of the decimal dot) is `3`, so we can have **3 decimal digits** for these numbers in the `money` field. This means that we will have **2 digits for the integer part** and **3 digits for the decimal part**.
56+
57+
✅ So, for example, these are all valid numbers for the `money` field:
58+
59+
* `12.345`
60+
* `12.3`
61+
* `12`
62+
* `1.2`
63+
* `0.123`
64+
* `0`
65+
66+
🚫 But these are all invalid numbers for that `money` field:
67+
68+
* `1.2345`
69+
* This number has more than 3 decimal places.
70+
* `123.234`
71+
* This number has more than 5 digits in total (integer and decimal part).
72+
* `123`
73+
* 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.
74+
75+
!!! tip
76+
Make sure you adjust the number of digits and decimal places for your own needs, in your own application. 🤓
77+
78+
## Create models with Decimals
79+
80+
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).
81+
82+
```Python hl_lines="4-6"
83+
# Code above omitted 👆
84+
85+
{!./docs_src/advanced/decimal/tutorial001.py[ln:25-35]!}
86+
87+
# Code below omitted 👇
88+
```
89+
90+
<details>
91+
<summary>👀 Full file preview</summary>
92+
93+
```Python
94+
{!./docs_src/advanced/decimal/tutorial001.py!}
95+
```
96+
97+
</details>
98+
99+
## Select Decimal data
100+
101+
Then, when working with Decimal types, you can confirm that they indeed avoid those rounding errors from floats:
102+
103+
```Python hl_lines="15-16"
104+
# Code above omitted 👆
105+
106+
{!./docs_src/advanced/decimal/tutorial001.py[ln:38-51]!}
107+
108+
# Code below omitted 👇
109+
```
110+
111+
<details>
112+
<summary>👀 Full file preview</summary>
113+
114+
```Python
115+
{!./docs_src/advanced/decimal/tutorial001.py!}
116+
```
117+
118+
</details>
119+
120+
## Review the results
121+
122+
Now if you run this, instead of printing the unexpected number `3.3000000000000003`, it prints `3.300`:
123+
124+
<div class="termy">
125+
126+
```console
127+
$ python app.py
128+
129+
// Some boilerplate and previous output omitted 😉
130+
131+
// The type of money is Decimal('1.100')
132+
Hero 1: id=1 secret_name='Dive Wilson' age=None name='Deadpond' money=Decimal('1.100')
133+
134+
// More output omitted here 🤓
135+
136+
// The type of money is Decimal('1.100')
137+
Hero 2: id=3 secret_name='Tommy Sharp' age=48 name='Rusty-Man' money=Decimal('2.200')
138+
139+
// No rounding errors, just 3.3! 🎉
140+
Total money: 3.300
141+
```
142+
143+
</div>
144+
145+
!!! warning
146+
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.
147+
148+
But decimals are supported by most of the other SQL databases. 🎉

docs/advanced/index.md

+4-6
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
# Advanced User Guide
22

3-
The **Advanced User Guide** will be coming soon to a <del>theater</del> **documentation** near you... 😅
3+
The **Advanced User Guide** is gradually growing, you can already read about some advanced topics.
44

5-
I just have to `add` it, `commit` it, and `refresh` it. 😉
5+
At some point it will include:
66

7-
It will include:
8-
9-
* How to use the `async` and `await` with the async session.
7+
* How to use `async` and `await` with the async session.
108
* How to run migrations.
119
* How to combine **SQLModel** models with SQLAlchemy.
12-
* ...and more.
10+
* ...and more. 🤓

docs/contributing.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ $ poetry shell
4242

4343
</div>
4444

45-
That will set up the environment variables needed dand will start a new shell with them.
45+
That will set up the environment variables needed and start a new shell with them.
4646

4747
#### Using your local SQLModel
4848

docs/databases.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ Some examples of databases that work like this could be **PostgreSQL**, **MySQL*
8585

8686
### Distributed servers
8787

88-
In some cases, the database could even be a group server applications running on different machines, working together and communicating between them to be more efficient and handle more data.
88+
In some cases, the database could even be a group of server applications running on different machines, working together and communicating between them to be more efficient and handle more data.
8989

9090
In this case, your code would talk to one or more of these server applications running on different machines.
9191

@@ -250,7 +250,7 @@ As these **primary key** IDs can uniquely identify each row on the table for tea
250250

251251
<img alt="table relationships" src="/img/databases/relationships.svg">
252252

253-
So, in the table for heroes, we use the `team_id` column to define a relationship to the *foreign* table for teams. Each value in the `team_id` column on the table with heroes will be the same value as the `id` column of one row in the table wiwth teams.
253+
So, in the table for heroes, we use the `team_id` column to define a relationship to the *foreign* table for teams. Each value in the `team_id` column on the table with heroes will be the same value as the `id` column of one row in the table with teams.
254254

255255
In the table for heroes we have a **primary key** that is the `id`. But we also have another column `team_id` that refers to a **key** in a **foreign** table. There's a technical term for that too, the `team_id` is a "**foreign key**".
256256

@@ -274,7 +274,7 @@ The language is called **SQL**, the name comes from for **Structured Query Langu
274274

275275
Nevertheless, the language is not only used to *query* for data. It is also used to create records/rows, to update them, to delete them. And to manipulate the database, create tables, etc.
276276

277-
This language is supported by all these databases that handle multiple tables, that's why they are called **SQL Databases**. Although, each database has small variations in the SQL language they support.
277+
This language is supported by all these databases that handle multiple tables, that's why they are called **SQL Databases**. Although, each database has small variations in the SQL language they support (*dialect*).
278278

279279
Let's imagine that the table holding the heroes is called the `hero` table. An example of a SQL query to get all the data from it could look like:
280280

docs/db-to-code.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ If the user provides this ID:
143143
2
144144
```
145145

146-
...the would be this table (with a single row):
146+
...the result would be this table (with a single row):
147147

148148
<table>
149149
<tr>

docs/help.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ And there are several ways to get help too.
1212

1313
## Subscribe to the FastAPI and Friends newsletter
1414

15-
You can subscribe to the (infrequent) [**FastAPI and friends** newsletter](/newsletter/){.internal-link target=_blank} to stay updated about:
15+
You can subscribe to the (infrequent) <a href="https://fastapi.tiangolo.com/newsletter" class="external-link" target="_blank">**FastAPI and friends** newsletter</a> to stay updated about:
1616

1717
* News about FastAPI and friends, including SQLModel 🚀
1818
* Guides 📝

0 commit comments

Comments
 (0)