You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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 <ahref="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 <ahref="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 <ahref="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:
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).
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. 🎉
Copy file name to clipboardExpand all lines: docs/databases.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -85,7 +85,7 @@ Some examples of databases that work like this could be **PostgreSQL**, **MySQL*
85
85
86
86
### Distributed servers
87
87
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.
89
89
90
90
In this case, your code would talk to one or more of these server applications running on different machines.
91
91
@@ -250,7 +250,7 @@ As these **primary key** IDs can uniquely identify each row on the table for tea
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.
254
254
255
255
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**".
0 commit comments