|
| 1 | +# Column Types |
| 2 | + |
| 3 | +In the tutorial, we stored scalar data types in our tables, like strings, numbers and timestamps. In practice, we often |
| 4 | +work with more complicated types that need to be converted to a data type our database supports. |
| 5 | + |
| 6 | +## Customising String Field Lengths |
| 7 | + |
| 8 | +As we discussed in [`TEXT` or `VARCHAR`](../tutorial/create-db-and-table.md#text-or-varchar), a `str` field type will be |
| 9 | +created as a `VARCHAR`, which has varying maximum-lengths depending on the database engine you are using. |
| 10 | + |
| 11 | +For cases where you know you only need to store a certain length of text, string field maximum length can be reduced |
| 12 | +using the `max_length` validation argument to `Field()`: |
| 13 | + |
| 14 | +```Python hl_lines="11" |
| 15 | +{!./docs_src/advanced/column_types/tutorial001.py[ln:1-12]!} |
| 16 | +``` |
| 17 | + |
| 18 | +/// details | 👀 Full file preview |
| 19 | + |
| 20 | +```Python |
| 21 | +{!./docs_src/advanced/column_types/tutorial001.py!} |
| 22 | +``` |
| 23 | + |
| 24 | +/// |
| 25 | + |
| 26 | +/// warning |
| 27 | + |
| 28 | +Database engines behave differently when you attempt to store longer text than the character length of the `VARCHAR` |
| 29 | +column. Notably: |
| 30 | + |
| 31 | +* SQLite does not enforce the length of a `VARCHAR`. It will happily store up to 500-million characters of text. |
| 32 | +* MySQL will emit a warning, but will also truncate your text to fit the size of the `VARCHAR`. |
| 33 | +* PostgreSQL will respond with an error code, and your query will not be executed. |
| 34 | + |
| 35 | +/// |
| 36 | + |
| 37 | +However if you need to store much longer strings than `VARCHAR` can allow, databases provide `TEXT` or `CLOB` |
| 38 | +(**c**haracter **l**arge **ob**ject) column types. We can use these by specifying an SQLAlchemy column type to the field |
| 39 | +with the `sa_type` keyword argument: |
| 40 | + |
| 41 | +```Python hl_lines="12" |
| 42 | +{!./docs_src/advanced/column_types/tutorial001.py[ln:5-45]!} |
| 43 | +``` |
| 44 | + |
| 45 | +/// tip |
| 46 | + |
| 47 | +`Text` also accepts a character length argument, which databases use to optimise the storage of a particular field. |
| 48 | +Some databases support `TINYTEXT`, `SMALLTEXT`, `MEDIUMTEXT` and `LONGTEXT` column types - ranging from 255 bytes to |
| 49 | +4 gigabytes. If you know the maximum length of data, specifying it like `Text(1000)` will automatically select the |
| 50 | +best-suited, supported type for your database engine. |
| 51 | + |
| 52 | +/// |
| 53 | + |
| 54 | + |
| 55 | +With this approach, we can use [any kind of SQLAlchemy type](https://docs.sqlalchemy.org/en/20/core/type_basics.html). |
| 56 | +For example, if we were building a mapping application, we could store spatial information: |
| 57 | + |
| 58 | +```Python |
| 59 | +{!./docs_src/advanced/column_types/tutorial002.py!} |
| 60 | +``` |
| 61 | + |
| 62 | +## Supported Types |
| 63 | + |
| 64 | +Python types are mapped to column types as so: |
| 65 | + |
| 66 | +<table> |
| 67 | +<tr> |
| 68 | +<th>Python type</th><th>SQLAlchemy type</th><th>Database column types</th> |
| 69 | +</tr> |
| 70 | +<tr> |
| 71 | +<td>str</td><td>String</td><td>VARCHAR</td> |
| 72 | +</tr> |
| 73 | +<tr> |
| 74 | +<td>int</td><td>Integer</td><td>INTEGER</td> |
| 75 | +</tr> |
| 76 | +<tr> |
| 77 | +<td>float</td><td>Float</td><td>FLOAT, REAL, DOUBLE</td> |
| 78 | +</tr> |
| 79 | +<tr> |
| 80 | +<td>bool</td><td>Boolean</td><td>BOOL or TINYINT</td> |
| 81 | +</tr> |
| 82 | +<tr> |
| 83 | +<td>datetime.datetime</td><td>DateTime</td><td>DATETIME, TIMESTAMP, DATE</td> |
| 84 | +</tr> |
| 85 | +<tr> |
| 86 | +<td>datetime.date</td><td>Date</td><td>DATE</td> |
| 87 | +</tr> |
| 88 | +<tr> |
| 89 | +<td>datetime.timedelta</td><td>Interval</td><td>INTERVAL, INT</td> |
| 90 | +</tr> |
| 91 | +<tr> |
| 92 | +<td>datetime.time</td><td>Time</td><td>TIME, DATETIME</td> |
| 93 | +</tr> |
| 94 | +<tr> |
| 95 | +<td>bytes</td><td>LargeBinary</td><td>BLOB, BYTEA</td> |
| 96 | +</tr> |
| 97 | +<tr> |
| 98 | +<td>Decimal</td><td>Numeric</td><td>DECIMAL, FLOAT</td> |
| 99 | +</tr> |
| 100 | +<tr> |
| 101 | +<td>enum.Enum</td><td>Enum</td><td>ENUM, VARCHAR</td> |
| 102 | +</tr> |
| 103 | +<tr> |
| 104 | +<td>uuid.UUID</td><td>GUID</td><td>UUID, CHAR(32)</td> |
| 105 | +</tr> |
| 106 | +</table> |
| 107 | + |
| 108 | +In addition, the following types are stored as `VARCHAR`: |
| 109 | + |
| 110 | +* ipaddress.IPv4Address |
| 111 | +* ipaddress.IPv4Network |
| 112 | +* ipaddress.IPv6Address |
| 113 | +* ipaddress.IPv6Network |
| 114 | +* pathlib.Path |
| 115 | + |
| 116 | +### IP Addresses |
| 117 | + |
| 118 | +IP Addresses from the <a href="https://docs.python.org/3/library/ipaddress.html" class="external-link" target="_blank">Python `ipaddress` module</a> are stored as text. |
| 119 | + |
| 120 | +```Python hl_lines="1 11" |
| 121 | +{!./docs_src/advanced/column_types/tutorial003.py[ln:1-13]!} |
| 122 | +``` |
| 123 | + |
| 124 | +### Filesystem Paths |
| 125 | + |
| 126 | +Paths to files and directories using the <a href="https://docs.python.org/3/library/pathlib.html" class="external-link" target="_blank">Python `pathlib` module</a> are stored as text. |
| 127 | + |
| 128 | +```Python hl_lines="3 12" |
| 129 | +{!./docs_src/advanced/column_types/tutorial003.py[ln:1-13]!} |
| 130 | +``` |
| 131 | + |
| 132 | +/// tip |
| 133 | + |
| 134 | +The stored value of a Path is the basic string value: `str(Path('../path/to/file'))`. If you need to store the full path |
| 135 | +ensure you call `absolute()` on the path before setting it in your model. |
| 136 | + |
| 137 | +/// |
| 138 | + |
| 139 | +### UUIDs |
| 140 | + |
| 141 | +UUIDs from the <a href="https://docs.python.org/3/library/uuid.html" class="external-link" target="_blank">Python `uuid` |
| 142 | +module</a> are stored as `UUID` types in supported databases (just PostgreSQL at the moment), otherwise as a `CHAR(32)`. |
| 143 | + |
| 144 | +```Python hl_lines="4 10" |
| 145 | +{!./docs_src/advanced/column_types/tutorial003.py[ln:1-13]!} |
| 146 | +``` |
| 147 | + |
| 148 | +## Custom Pydantic types |
| 149 | + |
| 150 | +As SQLModel is built on Pydantic, you can use any custom type as long as it would work in a Pydantic model. However, if |
| 151 | +the type is not a subclass of [a type from the table above](#supported-types), you will need to specify an SQLAlchemy |
| 152 | +type to use. |
0 commit comments