From 0adf7a9485e37b36e6c3a1a19361cb2c82061efc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Tue, 28 Dec 2021 10:34:53 +0100 Subject: [PATCH 1/9] =?UTF-8?q?=F0=9F=90=9B=20Make=20columns=20not=20have?= =?UTF-8?q?=20indexes=20by=20default?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sqlmodel/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlmodel/main.py b/sqlmodel/main.py index 08eaf5956f..12f30ba129 100644 --- a/sqlmodel/main.py +++ b/sqlmodel/main.py @@ -426,7 +426,7 @@ def get_column_from_field(field: ModelField) -> Column: # type: ignore nullable = not field.required index = getattr(field.field_info, "index", Undefined) if index is Undefined: - index = True + index = False if hasattr(field.field_info, "nullable"): field_nullable = getattr(field.field_info, "nullable") if field_nullable != Undefined: From 60614cdeb200eaf770b9fca312f547c03efc5798 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Tue, 28 Dec 2021 10:35:48 +0100 Subject: [PATCH 2/9] =?UTF-8?q?=F0=9F=8D=B1=20Add=20diagrams=20for=20expla?= =?UTF-8?q?ining=20indexes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../img/tutorial/indexes/dictionary001.drawio | 97 +++++++++++++++++ docs/img/tutorial/indexes/dictionary001.svg | 57 ++++++++++ .../img/tutorial/indexes/dictionary002.drawio | 97 +++++++++++++++++ docs/img/tutorial/indexes/dictionary002.svg | 1 + .../img/tutorial/indexes/dictionary003.drawio | 97 +++++++++++++++++ docs/img/tutorial/indexes/dictionary003.svg | 1 + .../img/tutorial/indexes/dictionary004.drawio | 100 +++++++++++++++++ docs/img/tutorial/indexes/dictionary004.svg | 1 + .../img/tutorial/indexes/dictionary005.drawio | 97 +++++++++++++++++ docs/img/tutorial/indexes/dictionary005.svg | 1 + .../img/tutorial/indexes/dictionary006.drawio | 100 +++++++++++++++++ docs/img/tutorial/indexes/dictionary006.svg | 1 + .../img/tutorial/indexes/dictionary007.drawio | 100 +++++++++++++++++ docs/img/tutorial/indexes/dictionary007.svg | 1 + .../img/tutorial/indexes/dictionary008.drawio | 103 ++++++++++++++++++ docs/img/tutorial/indexes/dictionary008.svg | 1 + docs/img/tutorial/indexes/techbook001.drawio | 92 ++++++++++++++++ docs/img/tutorial/indexes/techbook001.svg | 1 + 18 files changed, 948 insertions(+) create mode 100644 docs/img/tutorial/indexes/dictionary001.drawio create mode 100644 docs/img/tutorial/indexes/dictionary001.svg create mode 100644 docs/img/tutorial/indexes/dictionary002.drawio create mode 100644 docs/img/tutorial/indexes/dictionary002.svg create mode 100644 docs/img/tutorial/indexes/dictionary003.drawio create mode 100644 docs/img/tutorial/indexes/dictionary003.svg create mode 100644 docs/img/tutorial/indexes/dictionary004.drawio create mode 100644 docs/img/tutorial/indexes/dictionary004.svg create mode 100644 docs/img/tutorial/indexes/dictionary005.drawio create mode 100644 docs/img/tutorial/indexes/dictionary005.svg create mode 100644 docs/img/tutorial/indexes/dictionary006.drawio create mode 100644 docs/img/tutorial/indexes/dictionary006.svg create mode 100644 docs/img/tutorial/indexes/dictionary007.drawio create mode 100644 docs/img/tutorial/indexes/dictionary007.svg create mode 100644 docs/img/tutorial/indexes/dictionary008.drawio create mode 100644 docs/img/tutorial/indexes/dictionary008.svg create mode 100644 docs/img/tutorial/indexes/techbook001.drawio create mode 100644 docs/img/tutorial/indexes/techbook001.svg diff --git a/docs/img/tutorial/indexes/dictionary001.drawio b/docs/img/tutorial/indexes/dictionary001.drawio new file mode 100644 index 0000000000..659f6b52a4 --- /dev/null +++ b/docs/img/tutorial/indexes/dictionary001.drawio @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/img/tutorial/indexes/dictionary001.svg b/docs/img/tutorial/indexes/dictionary001.svg new file mode 100644 index 0000000000..b543793a25 --- /dev/null +++ b/docs/img/tutorial/indexes/dictionary001.svg @@ -0,0 +1,57 @@ +
A
A
B
B
C
C
D
D
E
E
F
F
G
G
H
H
I
I
J
J
K
K
L
L
M
M
N
N
O
O
P
P
Q
Q
R
R
S
S
T
T
U
U
V
V
W
W
X
X
Y
Y
Z
Z
Dictionary
Dictionary
M
M
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/docs/img/tutorial/indexes/dictionary002.drawio b/docs/img/tutorial/indexes/dictionary002.drawio new file mode 100644 index 0000000000..cb1857b1ad --- /dev/null +++ b/docs/img/tutorial/indexes/dictionary002.drawio @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/img/tutorial/indexes/dictionary002.svg b/docs/img/tutorial/indexes/dictionary002.svg new file mode 100644 index 0000000000..677687d248 --- /dev/null +++ b/docs/img/tutorial/indexes/dictionary002.svg @@ -0,0 +1 @@ +
A
A
B
B
C
C
D
D
E
E
F
F
G
G
H
H
I
I
J
J
K
K
L
L
M
M
N
N
O
O
P
P
Q
Q
R
R
S
S
T
T
U
U
V
V
W
W
X
X
Y
Y
Z
Z
Dictionary
Dictionary
M
M
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/docs/img/tutorial/indexes/dictionary003.drawio b/docs/img/tutorial/indexes/dictionary003.drawio new file mode 100644 index 0000000000..845eb065cd --- /dev/null +++ b/docs/img/tutorial/indexes/dictionary003.drawio @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/img/tutorial/indexes/dictionary003.svg b/docs/img/tutorial/indexes/dictionary003.svg new file mode 100644 index 0000000000..d667a68893 --- /dev/null +++ b/docs/img/tutorial/indexes/dictionary003.svg @@ -0,0 +1 @@ +
A
A
B
B
C
C
D
D
E
E
F
F
G
G
H
H
I
I
J
J
K
K
L
L
M
M
N
N
O
O
P
P
Q
Q
R
R
S
S
T
T
U
U
V
V
W
W
X
X
Y
Y
Z
Z
Dictionary
Dictionary
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/docs/img/tutorial/indexes/dictionary004.drawio b/docs/img/tutorial/indexes/dictionary004.drawio new file mode 100644 index 0000000000..14bbb1e26e --- /dev/null +++ b/docs/img/tutorial/indexes/dictionary004.drawio @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/img/tutorial/indexes/dictionary004.svg b/docs/img/tutorial/indexes/dictionary004.svg new file mode 100644 index 0000000000..f881d6c9c2 --- /dev/null +++ b/docs/img/tutorial/indexes/dictionary004.svg @@ -0,0 +1 @@ +
A
A
B
B
C
C
D
D
E
E
F
F
G
G
H
H
I
I
J
J
K
K
L
L
M
M
N
N
O
O
P
P
Q
Q
R
R
S
S
T
T
U
U
V
V
W
W
X
X
Y
Y
Z
Z
Dictionary
Dictionary
F
F
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/docs/img/tutorial/indexes/dictionary005.drawio b/docs/img/tutorial/indexes/dictionary005.drawio new file mode 100644 index 0000000000..9e339c177e --- /dev/null +++ b/docs/img/tutorial/indexes/dictionary005.drawio @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/img/tutorial/indexes/dictionary005.svg b/docs/img/tutorial/indexes/dictionary005.svg new file mode 100644 index 0000000000..9d376245c0 --- /dev/null +++ b/docs/img/tutorial/indexes/dictionary005.svg @@ -0,0 +1 @@ +
A
A
B
B
C
C
D
D
E
E
F
F
G
G
H
H
I
I
J
J
K
K
L
L
M
M
N
N
O
O
P
P
Q
Q
R
R
S
S
T
T
U
U
V
V
W
W
X
X
Y
Y
Z
Z
Dictionary
Dictionary
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/docs/img/tutorial/indexes/dictionary006.drawio b/docs/img/tutorial/indexes/dictionary006.drawio new file mode 100644 index 0000000000..3c669d323f --- /dev/null +++ b/docs/img/tutorial/indexes/dictionary006.drawio @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/img/tutorial/indexes/dictionary006.svg b/docs/img/tutorial/indexes/dictionary006.svg new file mode 100644 index 0000000000..30be80ea8b --- /dev/null +++ b/docs/img/tutorial/indexes/dictionary006.svg @@ -0,0 +1 @@ +
A
A
B
B
C
C
D
D
E
E
F
F
G
G
H
H
I
I
J
J
K
K
L
L
M
M
N
N
O
O
P
P
Q
Q
R
R
S
S
T
T
U
U
V
V
W
W
X
X
Y
Y
Z
Z
Dictionary
Dictionary
C
C
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/docs/img/tutorial/indexes/dictionary007.drawio b/docs/img/tutorial/indexes/dictionary007.drawio new file mode 100644 index 0000000000..89b32cabaf --- /dev/null +++ b/docs/img/tutorial/indexes/dictionary007.drawio @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/img/tutorial/indexes/dictionary007.svg b/docs/img/tutorial/indexes/dictionary007.svg new file mode 100644 index 0000000000..79e795060e --- /dev/null +++ b/docs/img/tutorial/indexes/dictionary007.svg @@ -0,0 +1 @@ +
A
A
B
B
C
C
D
D
E
E
F
F
G
G
H
H
I
I
J
J
K
K
L
L
M
M
N
N
O
O
P
P
Q
Q
R
R
S
S
T
T
U
U
V
V
W
W
X
X
Y
Y
Z
Z
Dictionary
Dictionary
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/docs/img/tutorial/indexes/dictionary008.drawio b/docs/img/tutorial/indexes/dictionary008.drawio new file mode 100644 index 0000000000..ac08ad04d4 --- /dev/null +++ b/docs/img/tutorial/indexes/dictionary008.drawio @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/img/tutorial/indexes/dictionary008.svg b/docs/img/tutorial/indexes/dictionary008.svg new file mode 100644 index 0000000000..013a4d64a3 --- /dev/null +++ b/docs/img/tutorial/indexes/dictionary008.svg @@ -0,0 +1 @@ +
A
A
B
B
C
C
D
D
E
E
F
F
G
G
H
H
I
I
J
J
K
K
L
L
M
M
N
N
O
O
P
P
Q
Q
R
R
S
S
T
T
U
U
V
V
W
W
X
X
Y
Y
Z
Z
Dictionary
Dictionary
D
D
Viewer does not support full SVG 1.1
\ No newline at end of file diff --git a/docs/img/tutorial/indexes/techbook001.drawio b/docs/img/tutorial/indexes/techbook001.drawio new file mode 100644 index 0000000000..de1c25668c --- /dev/null +++ b/docs/img/tutorial/indexes/techbook001.drawio @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/img/tutorial/indexes/techbook001.svg b/docs/img/tutorial/indexes/techbook001.svg new file mode 100644 index 0000000000..8b0c09ddcf --- /dev/null +++ b/docs/img/tutorial/indexes/techbook001.svg @@ -0,0 +1 @@ +
Technical Book
Technical Book
Chapter 1
Chapter 1
Chapter 2
Chapter 2
Chapter 3
Chapter 3
Chapter 4
Chapter 4
Chapter 5
Chapter 5
Chapter 6
Chapter 6
Chapter 7
Chapter 7
Book Index
Book Index
Database
Database
Python
Python
Files
Files
Editors
Editors
Viewer does not support full SVG 1.1
\ No newline at end of file From 53dc47666310dce6e14b648f98e2dec916d67dcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Tue, 28 Dec 2021 10:43:59 +0100 Subject: [PATCH 3/9] =?UTF-8?q?=F0=9F=93=9D=20Add=20docs=20for=20indexes?= =?UTF-8?q?=20explaining=20them=20and=20how=20to=20declare=20them?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/tutorial/indexes.md | 406 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 406 insertions(+) create mode 100644 docs/tutorial/indexes.md diff --git a/docs/tutorial/indexes.md b/docs/tutorial/indexes.md new file mode 100644 index 0000000000..6513d7d462 --- /dev/null +++ b/docs/tutorial/indexes.md @@ -0,0 +1,406 @@ +# Indexes - Optimize Queries + +We just saw how to get some data `WHERE` a **condition** is true. For example, where the hero **name is "Deadpond"**. + +If we just create the tables and the data as we have been doing, when we `SELECT` some data using `WHERE`, the database would have to **scan** through **each one of the records** to find the ones that **match**. This is not a problem with 3 heroes as in these examples. + +But imagine that your database has **thousands** or **millions** of **records**, if every time you want to find the heroes with the name "Deadpond" it has to scan through **all** of the records to find all the possible matches, then that becomes problematic, as it would be too slow. + +I'll show you how to handle it with a database **index**. + +The change in the code is **extremely small**, but it's useful to understand what's happening behind the scenes, so I'll show you **how it all works** and what it means. + +--- + +If you already executed the previous examples and have a database with data, **remove the database file** before running each example, that way you won't have duplicate data and you will be able to get the same results. + +## No Time to Explain + +Are you already a **SQL expert** and don't have time for all my explanations? + +Fine, in that case, you can **sneak peek** the final code to create indexes here. + +
+👀 Full file preview + +```Python hl_lines="8 10" +{!./docs_src/tutorial/indexes/tutorial002.py!} +``` + +
+ +..but if you are not an expert, **continue reading**, this will probably be useful. 🤓 + +## What is an Index + +In general, an **index** is just something we can have to help us **find things faster**. It normally works by having things in **order**. Let's think about some real-life examples before even thinking about databases and code. + +### An Index and a Dictionary + +Imagine a **dictionary**, a book with definitions of words. 📔 ...not a Python `dict`. 😅 + +Let's say that you want to **find a word**, for example the word "**database**". You take the dictionary, and open it somewhere, for example in the middle. Maybe you see some definitions of words that start with `m`, like `manual`, so you conclude that you are in the letter `m` in the dictionary. + + + +You know that in the alphabet, the letter `d` for `database` comes **before** the letter `m` for `manual`. + + + +So, you know you have to search in the dictionary **before** the point you currently are. You still don't know where the word `database` is, because you don't know exactly where the letter `d` is in the dictionary, but you know that **it is not after** that point, you can now **discard the right half** of the dictionary in your search. + + + +Next, you **open the dictionary again**, but only taking into account the **half of the dictionary** that can contain the word you want, the **left part of the dictionary**. You open it in the middle of that left part and now you arrive maybe at the letter `f`. + + + +You know that `d` from `database` comes before `f`. So it has to be **before** that. But now you know that `database` **is not after** that point, and you can discard the dictionary from that point onward. + + + +Now you have a **small section of dictionary** to search (only a **quarter** of dictionary can have your word). You take that **quarter** of the pages at the start of the dictionary that can contain your word, and open it in the middle of that section. Maybe you arrive at the letter `c`. + + + +You know the word `database` has to be **after** that and **not before** that point, so you can discard the left part of that block of pages. + + + +You repeat this process **a few more times**, and you finally arrive at the letter `d`, you continue with the same process in that section for the letter `d` and you finally **find the word** `database`. 🎉 + + + +You had to open the dictionary a few times, maybe **5 or 10**. That's actually **very little work** compared to what it could have been. + +!!! note "Technical Details" + Do you like **fancy words**? Cool! Programmers tend to like fancy words. 😅 + + That algorithm I showed you above is called **Binary Search**. + + It's called like that because you **search** something by splitting the dictionary (or any ordered list of things) in **two** ("binary" means "two") parts. And you do that process multiple times until you find what you want. + +### An Index and a Novel + +Let's now imagine you are reading a **novel book**. And someone told you that at some point, they mention a **database**, and you want to find that chapter. + +How do you find the word "*database*" there? You might have to read **the entire book** to find where the word "*database*" is located in the book. So, instead of opening the book 5 or 10 times, you would have to open each of the **500 pages** and read them one by one until you find the word. You might enjoy the book, though. 😅 + +But if we are only interested in **quickly finding information** (as when working with SQL databases), then reading each of the 500 pages is **too inefficient** when there could be an option to open the book in 5 or 10 places and find what you're looking for. + +### A Technical Book with an Index + +Now let's imagine you are reading a technical book. For example, with several topics about programming. And there's a couple of sections where it talks about a **database**. + +This book might have a **book index**: a section in the book that has some **names of topics covered** and the **page numbers** in the book where you can read about them. And the topic names are **sorted** in alphabetic order, pretty much like a dictionary (a book with words, as in the previous example). + +In this case, you can open that book in the end (or in the beginning) to find the **book index** section, it would have only a few pages. And then, you can do the same process as with the **dictionary** example above. + +Open the index, and after **5 or 10 steps**, quickly find the topic "**database**" with the page numbers where that is covered, for example "page 253 in Chapter 5". Now you used the dictionary technique to find the **topic**, and that topic gave you a **page number**. + +Now you know that you need to find "**page 253**". But by looking at the closed book you still don't know where that page is, so you have to **find that page**. To find it, you can do the same process again, but this time, instead of searching for a **topic** in the **index**, you are searching for a **page number** in the **entire book**. And after **5 or 10 more steps**, you find the page 253 in Chapter 5. + + + +After this, even though this book is not a dictionary and has some particular content, you were able to **find the section** in the book that talks about a "**database**" in a **few steps** (say 10 or 20, instead of reading all the 500 pages). + +The main point is that the index is **sorted**, so we can use the same process we used for the **dictionary** to find the topic. And then that gives us a page number, and the **page numbers are also sorted**! 😅 + +When we have a list of sorted things we can apply the same technique, and that's the whole trick here, we use the same technique first for the **topics** in the index and then for the **page numbers** to find the actual chapter. + +Such efficiency! 😎 + +## What are Database Indexes + +**Database indexes** are very similar to **book indexes**. + +Database indexes store some info, some keys, in a way that makes it **easy and fast to find** (for example sorted), and then for each key they **point to some data somewhere else** in the database. + +Let's see a more clear example. Let's say you have this table in a database: + + + + + + + + + + + + + + +
idnamesecret_nameage
1DeadpondDive Wilsonnull
2Spider-BoyPedro Parqueadornull
3Rusty-ManTommy Sharp48
+ +And let's imagine you have **many more rows**, many more heroes. Probably **thousands**. + +If you tell the SQL database to get you a hero by a specific name, for example `Spider-Boy` (by using the `name` in the `WHERE` part of the SQL query), the database will have to **scan** all the heroes, checking **one by one** to find all the ones with a name of `Spider-Boy`. + +In this case, there's only one, but there's nothing limiting the database from having **more records with the same name**. And because of that, the database would **continue searching** and checking each one of the records, which would be very slow. + +But now let's say that the database has an index for the column `name`. The index could look something like this, we could imagine that the index is like an additional special table that the database manages automatically: + + + + + + + + + + + + + + +
nameid
Deadpond1
Rusty-Man3
Spider-Boy2
+ +It would have each `name` field from the `hero` table **in order**. It would not be sorted by `id`, but by `name` (in alphabetical order, as the `name` is a string). So, first it would have `Deadpond`, then `Rusty-Man`, and last `Spider-Boy`. It would also include the `id` of each hero. Remember that this could have **thousands** of heroes. + +Then the database would be able to use more or less the same ideas in the examples above with the **dictionary** and the **book index**. + +It could start somewhere (for example, in the middle of the index). It could arrive at some hero there in the middle, like `Rusty-Man`. And because the **index** has the `name` fields in order, the database would know that it can **discard all the previous index rows** and **only search** in the following index rows. + + + + + + + + + + + + + + +
nameid
Deadpond1
Rusty-Man3
Spider-Boy2
+ +And that way, as with the example with the dictionary above, **instead of reading thousands of heroes**, the database would be able to do a few steps, say **5 or 10 steps**, and arrive at the row of the index that has `Spider-Boy`, even if the table (and index) has thousands of rows: + + + + + + + + + + + + + + +
nameid
Deadpond1
Rusty-Man3
✨ Spider-Boy ✨2
+ +Then by looking at **this index row**, it would know that the `id` for `Spider-Boy` in the `hero` table is `2`. + +So then it could **search that `id`** in the `hero` table using more or less the **same technique**. + +That way, in the end, instead of reading thousands of records, the database only had to do **a few steps** to find the hero we wanted. + +## Updating the Index + +As you can imagine, for all this to work, the index would need to be **up to date** with the data in the database. + +If you had to update it **manually** in code, it would be very cumbersome and **error-prone**, as it would be easy to end up in a state where the index is not up to date and points to incorrect data. 😱 + +Here's the good news: when you create an index in a **SQL Database**, the database takes care of **updating** it **automatically** whenever it's necessary. 😎🎉 + +If you **add new records** to the `hero` table, the database will **automatically** update the index. It will do the **same process** of **finding** the right place to put the new index data (those **5 or 10 steps** described above), and then it will save the new index information there. The same would happen when you **update** or **delete** data. + +Defining and creating an index is very **easy** with SQL databases. And then **using it** is even easier... it's transparent. The database will figure out which index to use automatically, the SQL queries don't even change. + +So, in SQL databases **indexes are great**! And are super **easy to use**. Why not just have indexes for everything? .....Because indexes also have a "**cost**" in computation and storage (disk space). + +## Index Cost + +There's a **cost** associated with **indexes**. 💰 + +When you don't have an index and add a **new row** to the table `hero`, the database has to perform **1 operation** to add the new hero row at the end of the table. + +But if you have an **index** for the **hero names**, now the database has to perform the same **1 operation** to add that row **plus** some extra **5 or 10 operations** in the index, to find the right spot for the name, to then add that **index record** there. + +And if you have an index for the `name`, one for the `age`, and one for the `secret_name`, now the database has to perform the same **1 operation** to add that row **plus** some extra **5 or 10 operations** in the index **times 3**, for each of the indexes. This means that now adding one row takes something like **31 operations**. + +This also means that you are **exchanging** the time it takes to **read** data for the time it takes to **write** data plus some extra **space** in the database. + +If you have queries that get data out of the database comparing each one of those fields (for example using `WHERE`), then it makes total sense to have indexes for each one of them. Because **31 operations** while creating or updating data (plus the space of the index) is much, much better than the possible **500 or 1000 operations** to read all the rows to be able to compare them using each field. + +But if you **never** have queries that find records by the `secret_name` (you never use `secret_name` in the `WHERE` part) it probably doesn't make sense to have an index for the `secret_name` field/column, as that will increase the computational and space **cost** of writing and updating the database. + +## Create an Index with SQL + +Phew, that was a lot of theory and explanations. 😅 + +The most important thing about indexes is **understanding** them, how, and when to use them. + +Let's now see the **SQL** syntax to create an **index**. It is very simple: + +```SQL hl_lines="3" +CREATE INDEX ix_hero_name +ON hero (name) +``` + +This means, more or less: + +> Hey SQL database 👋, please `CREATE` an `INDEX` for me. +> +> I want the name of the index to be `ix_hero_name`. +> +> This index should be `ON` the table `hero`, it refers to that table. +> +> The column I want you to use for it is `name`. + +## Declare Indexes with SQLModel + +And now let's see how to define indexes in **SQLModel**. + +The change in code is underwhelming, it's very simple. 😆 + +Here's the `Hero` model we had before: + +```Python hl_lines="8" +{!./docs_src/tutorial/where/tutorial001.py[ln:1-10]!} + +# Code below omitted 👇 +``` + +
+👀 Full file preview + +```Python +{!./docs_src/tutorial/where/tutorial001.py!} +``` + +
+ +Let's now update it to tell **SQLModel** to create an index for the `name` field when creating the table: + +```Python hl_lines="8" +{!./docs_src/tutorial/indexes/tutorial001.py[ln:1-10]!} + +# Code below omitted 👇 +``` + +
+👀 Full file preview + +```Python +{!./docs_src/tutorial/indexes/tutorial001.py!} +``` + +
+ +We use the same `Field()` again as we did before, and set `index=True`. That's it! 🚀 + +Notice that we didn't set an argument of `default=None` or anything similar. This means that **SQLModel** (thanks to Pydantic) will keep it as a **required** field. + +!!! info + SQLModel (actually SQLAlchemy) will **automatically generate the index name** for you. + + In this case the generated name would be `ix_hero_name`. + +## Query Data + +Now, to query the data using the field `name` and the new index we don't have to do anything special or different in the code, it's just **the same code**. + +The SQL database will figure it out **automatically**. ✨ + +This is great because it means that indexes are very **simple to use**. But it might also feel counterintuitive at first, as you are **not doing anything** explicitly in the code to make it obvious that the index is useful, it all happens in the database behind the scenes. + +```Python hl_lines="5" +# Code above omitted 👆 + +{!./docs_src/tutorial/indexes/tutorial001.py[ln:36-41]!} + +# Code below omitted 👇 +``` + +
+👀 Full file preview + +```Python +{!./docs_src/tutorial/indexes/tutorial001.py!} +``` + +
+ +This is exactly the same code as we had before, but now the database will **use the index** underneath. + +## Run the Program + +If you run the program now, you will see an output like this: + +
+ +```console +$ python app.py + +// Some boilerplate output omitted 😉 + +// Create the table +CREATE TABLE hero ( + id INTEGER, + name VARCHAR NOT NULL, + secret_name VARCHAR NOT NULL, + age INTEGER, + PRIMARY KEY (id) +) + +// Create the index 🤓🎉 +CREATE INDEX ix_hero_name ON hero (name) + +// The SELECT with WHERE looks the same +INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age +FROM hero +WHERE hero.name = ? +INFO Engine [no key 0.00014s] ('Deadpond',) + +// The resulting hero +secret_name='Dive Wilson' age=None id=1 name='Deadpond' +``` + +
+ +## More Indexes + +We are going to query the `hero` table doing comparisons on the `age` field too, so we should **define an index** for that one as well: + +```Python hl_lines="10" +{!./docs_src/tutorial/indexes/tutorial002.py[ln:1-10]!} + +# Code below omitted 👇 +``` + +
+👀 Full file preview + +```Python +{!./docs_src/tutorial/indexes/tutorial002.py!} +``` + +
+ +In this case, we want the default value of `age` to continue being `None`, so we set `default=None` when using `Field()`. + +Now when we use **SQLModel** to create the database and tables, it will also create the **indexes** for these two columns in the `hero` table. + +So, when we query the database for the `hero` table and use those **two columns** to define what data we get, the database will be able to **use those indexes** to improve the **reading performance**. 🚀 + +## Primary Key and Indexes + +You probably noticed that we didn't set `index=True` for the `id` field. + +Because the `id` is already the **primary key**, the database will automatically create an internal **index** for it. + +The database always creates an internal index for **primary keys** automatically, as those are the primary way to organize, store, and retrieve data. 🤓 + +But if you want to be **frequently querying** the SQL database for any **other field** (e.g. using any other field in the `WHERE` section), you will probably want to have at least an **index** for that. + +## Recap + +**Indexes** are very important to improve **reading performance** and speed when querying the database. 🏎 + +Creating and using them is very **simple** and easy. The most important part is to understand **how** they work, **when** to create them, and for **which columns**. From 0581a84d7196f5c0f801560ee14ceea3c63b0f13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Tue, 28 Dec 2021 10:44:43 +0100 Subject: [PATCH 4/9] =?UTF-8?q?=F0=9F=93=9D=20Add=20source=20examples=20fo?= =?UTF-8?q?r=20creating=20indexes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_src/tutorial/indexes/__init__.py | 0 docs_src/tutorial/indexes/tutorial001.py | 51 ++++++++++++++++++++ docs_src/tutorial/indexes/tutorial002.py | 59 ++++++++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 docs_src/tutorial/indexes/__init__.py create mode 100644 docs_src/tutorial/indexes/tutorial001.py create mode 100644 docs_src/tutorial/indexes/tutorial002.py diff --git a/docs_src/tutorial/indexes/__init__.py b/docs_src/tutorial/indexes/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs_src/tutorial/indexes/tutorial001.py b/docs_src/tutorial/indexes/tutorial001.py new file mode 100644 index 0000000000..539220c9b7 --- /dev/null +++ b/docs_src/tutorial/indexes/tutorial001.py @@ -0,0 +1,51 @@ +from typing import Optional + +from sqlmodel import Field, Session, SQLModel, create_engine, select + + +class Hero(SQLModel, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + name: str = Field(index=True) + secret_name: str + age: Optional[int] = Field(default=None, index=True) + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +engine = create_engine(sqlite_url, echo=True) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +def create_heroes(): + hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson") + hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador") + hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + + session.commit() + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero).where(Hero.name == "Deadpond") + results = session.exec(statement) + for hero in results: + print(hero) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() diff --git a/docs_src/tutorial/indexes/tutorial002.py b/docs_src/tutorial/indexes/tutorial002.py new file mode 100644 index 0000000000..ebc8d64f65 --- /dev/null +++ b/docs_src/tutorial/indexes/tutorial002.py @@ -0,0 +1,59 @@ +from typing import Optional + +from sqlmodel import Field, Session, SQLModel, create_engine, select + + +class Hero(SQLModel, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + name: str = Field(index=True) + secret_name: str + age: Optional[int] = Field(default=None, index=True) + + +sqlite_file_name = "database.db" +sqlite_url = f"sqlite:///{sqlite_file_name}" + +engine = create_engine(sqlite_url, echo=True) + + +def create_db_and_tables(): + SQLModel.metadata.create_all(engine) + + +def create_heroes(): + hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson") + hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador") + hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48) + hero_4 = Hero(name="Tarantula", secret_name="Natalia Roman-on", age=32) + hero_5 = Hero(name="Black Lion", secret_name="Trevor Challa", age=35) + hero_6 = Hero(name="Dr. Weird", secret_name="Steve Weird", age=36) + hero_7 = Hero(name="Captain North America", secret_name="Esteban Rogelios", age=93) + + with Session(engine) as session: + session.add(hero_1) + session.add(hero_2) + session.add(hero_3) + session.add(hero_4) + session.add(hero_5) + session.add(hero_6) + session.add(hero_7) + + session.commit() + + +def select_heroes(): + with Session(engine) as session: + statement = select(Hero).where(Hero.age <= 35) + results = session.exec(statement) + for hero in results: + print(hero) + + +def main(): + create_db_and_tables() + create_heroes() + select_heroes() + + +if __name__ == "__main__": + main() From fd78ba883e24ac733e3bdb8c53f0caa2e8784b2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Tue, 28 Dec 2021 10:45:34 +0100 Subject: [PATCH 5/9] =?UTF-8?q?=E2=9C=85=20Add=20tests=20for=20indexes=20a?= =?UTF-8?q?nd=20index=20creation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_tutorial/test_indexes/__init__.py | 0 .../test_indexes/test_tutorial001.py | 30 ++++++++++++++++++ .../test_indexes/test_tutorial006.py | 31 +++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 tests/test_tutorial/test_indexes/__init__.py create mode 100644 tests/test_tutorial/test_indexes/test_tutorial001.py create mode 100644 tests/test_tutorial/test_indexes/test_tutorial006.py diff --git a/tests/test_tutorial/test_indexes/__init__.py b/tests/test_tutorial/test_indexes/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_tutorial/test_indexes/test_tutorial001.py b/tests/test_tutorial/test_indexes/test_tutorial001.py new file mode 100644 index 0000000000..0c3fcf01ba --- /dev/null +++ b/tests/test_tutorial/test_indexes/test_tutorial001.py @@ -0,0 +1,30 @@ +from unittest.mock import patch + +from sqlalchemy import inspect +from sqlalchemy.engine.reflection import Inspector +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function + + +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.indexes import tutorial001 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == [ + [{"secret_name": "Dive Wilson", "age": None, "id": 1, "name": "Deadpond"}] + ] + + insp: Inspector = inspect(mod.engine) + indexes = insp.get_indexes(str(mod.Hero.__tablename__)) + assert indexes == [ + {"name": "ix_hero_name", "column_names": ["name"], "unique": 0}, + {"name": "ix_hero_age", "column_names": ["age"], "unique": 0}, + ] diff --git a/tests/test_tutorial/test_indexes/test_tutorial006.py b/tests/test_tutorial/test_indexes/test_tutorial006.py new file mode 100644 index 0000000000..1e81c606bf --- /dev/null +++ b/tests/test_tutorial/test_indexes/test_tutorial006.py @@ -0,0 +1,31 @@ +from unittest.mock import patch + +from sqlalchemy import inspect +from sqlalchemy.engine.reflection import Inspector +from sqlmodel import create_engine + +from ...conftest import get_testing_print_function + + +def test_tutorial(clear_sqlmodel): + from docs_src.tutorial.indexes import tutorial002 as mod + + mod.sqlite_url = "sqlite://" + mod.engine = create_engine(mod.sqlite_url) + calls = [] + + new_print = get_testing_print_function(calls) + + with patch("builtins.print", new=new_print): + mod.main() + assert calls == [ + [{"name": "Tarantula", "secret_name": "Natalia Roman-on", "age": 32, "id": 4}], + [{"name": "Black Lion", "secret_name": "Trevor Challa", "age": 35, "id": 5}], + ] + + insp: Inspector = inspect(mod.engine) + indexes = insp.get_indexes(str(mod.Hero.__tablename__)) + assert indexes == [ + {"name": "ix_hero_name", "column_names": ["name"], "unique": 0}, + {"name": "ix_hero_age", "column_names": ["age"], "unique": 0}, + ] From b1d06757465417ba2a5c8d0ae2684a3446fd7094 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Tue, 28 Dec 2021 11:03:08 +0100 Subject: [PATCH 6/9] =?UTF-8?q?=F0=9F=93=9D=20Update=20source=20examples?= =?UTF-8?q?=20to=20include=20explicit=20indexes=20in=20all=20the=20docs=20?= =?UTF-8?q?after=20indexes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_src/advanced/decimal/tutorial001.py | 4 ++-- docs_src/tutorial/code_structure/tutorial001/models.py | 6 +++--- .../tutorial/code_structure/tutorial002/hero_model.py | 4 ++-- .../tutorial/code_structure/tutorial002/team_model.py | 2 +- docs_src/tutorial/connect/create_tables/tutorial001.py | 6 +++--- docs_src/tutorial/connect/delete/tutorial001.py | 6 +++--- docs_src/tutorial/connect/insert/tutorial001.py | 6 +++--- docs_src/tutorial/connect/select/tutorial001.py | 6 +++--- docs_src/tutorial/connect/select/tutorial002.py | 6 +++--- docs_src/tutorial/connect/select/tutorial003.py | 6 +++--- docs_src/tutorial/connect/select/tutorial004.py | 6 +++--- docs_src/tutorial/connect/select/tutorial005.py | 6 +++--- docs_src/tutorial/connect/update/tutorial001.py | 6 +++--- docs_src/tutorial/delete/tutorial001.py | 4 ++-- docs_src/tutorial/delete/tutorial002.py | 4 ++-- .../tutorial/fastapi/app_testing/tutorial001/main.py | 4 ++-- docs_src/tutorial/fastapi/delete/tutorial001.py | 4 ++-- .../tutorial/fastapi/limit_and_offset/tutorial001.py | 4 ++-- .../tutorial/fastapi/multiple_models/tutorial001.py | 4 ++-- .../tutorial/fastapi/multiple_models/tutorial002.py | 4 ++-- docs_src/tutorial/fastapi/read_one/tutorial001.py | 4 ++-- docs_src/tutorial/fastapi/relationships/tutorial001.py | 6 +++--- .../tutorial/fastapi/response_model/tutorial001.py | 4 ++-- .../fastapi/session_with_dependency/tutorial001.py | 4 ++-- .../tutorial/fastapi/simple_hero_api/tutorial001.py | 4 ++-- docs_src/tutorial/fastapi/teams/tutorial001.py | 6 +++--- docs_src/tutorial/fastapi/update/tutorial001.py | 4 ++-- docs_src/tutorial/many_to_many/tutorial001.py | 6 +++--- docs_src/tutorial/many_to_many/tutorial002.py | 6 +++--- docs_src/tutorial/many_to_many/tutorial003.py | 6 +++--- docs_src/tutorial/offset_and_limit/tutorial001.py | 4 ++-- docs_src/tutorial/offset_and_limit/tutorial002.py | 4 ++-- docs_src/tutorial/offset_and_limit/tutorial003.py | 4 ++-- docs_src/tutorial/offset_and_limit/tutorial004.py | 4 ++-- docs_src/tutorial/one/tutorial001.py | 4 ++-- docs_src/tutorial/one/tutorial002.py | 4 ++-- docs_src/tutorial/one/tutorial003.py | 4 ++-- docs_src/tutorial/one/tutorial004.py | 4 ++-- docs_src/tutorial/one/tutorial005.py | 4 ++-- docs_src/tutorial/one/tutorial006.py | 4 ++-- docs_src/tutorial/one/tutorial007.py | 4 ++-- docs_src/tutorial/one/tutorial008.py | 4 ++-- docs_src/tutorial/one/tutorial009.py | 4 ++-- .../back_populates/tutorial001.py | 6 +++--- .../back_populates/tutorial002.py | 6 +++--- .../back_populates/tutorial003.py | 10 +++++----- .../create_and_update_relationships/tutorial001.py | 6 +++--- .../define_relationship_attributes/tutorial001.py | 6 +++--- .../read_relationships/tutorial001.py | 6 +++--- .../read_relationships/tutorial002.py | 6 +++--- docs_src/tutorial/update/tutorial001.py | 4 ++-- docs_src/tutorial/update/tutorial002.py | 4 ++-- docs_src/tutorial/update/tutorial003.py | 4 ++-- docs_src/tutorial/update/tutorial004.py | 4 ++-- 54 files changed, 131 insertions(+), 131 deletions(-) diff --git a/docs_src/advanced/decimal/tutorial001.py b/docs_src/advanced/decimal/tutorial001.py index 1b16770cc6..b803119d9e 100644 --- a/docs_src/advanced/decimal/tutorial001.py +++ b/docs_src/advanced/decimal/tutorial001.py @@ -6,9 +6,9 @@ class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) money: condecimal(max_digits=5, decimal_places=3) = Field(default=0) diff --git a/docs_src/tutorial/code_structure/tutorial001/models.py b/docs_src/tutorial/code_structure/tutorial001/models.py index 9bd1fa93f2..8e2647b3c4 100644 --- a/docs_src/tutorial/code_structure/tutorial001/models.py +++ b/docs_src/tutorial/code_structure/tutorial001/models.py @@ -5,7 +5,7 @@ class Team(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) headquarters: str heroes: List["Hero"] = Relationship(back_populates="team") @@ -13,9 +13,9 @@ class Team(SQLModel, table=True): class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) team_id: Optional[int] = Field(default=None, foreign_key="team.id") team: Optional[Team] = Relationship(back_populates="heroes") diff --git a/docs_src/tutorial/code_structure/tutorial002/hero_model.py b/docs_src/tutorial/code_structure/tutorial002/hero_model.py index 84fc7f276b..06dd9c6dfd 100644 --- a/docs_src/tutorial/code_structure/tutorial002/hero_model.py +++ b/docs_src/tutorial/code_structure/tutorial002/hero_model.py @@ -8,9 +8,9 @@ class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) team_id: Optional[int] = Field(default=None, foreign_key="team.id") team: Optional["Team"] = Relationship(back_populates="heroes") diff --git a/docs_src/tutorial/code_structure/tutorial002/team_model.py b/docs_src/tutorial/code_structure/tutorial002/team_model.py index 54974a01e5..c8a008bf4c 100644 --- a/docs_src/tutorial/code_structure/tutorial002/team_model.py +++ b/docs_src/tutorial/code_structure/tutorial002/team_model.py @@ -8,7 +8,7 @@ class Team(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) headquarters: str heroes: List["Hero"] = Relationship(back_populates="team") diff --git a/docs_src/tutorial/connect/create_tables/tutorial001.py b/docs_src/tutorial/connect/create_tables/tutorial001.py index 86dcc9adb8..ef24ec77d0 100644 --- a/docs_src/tutorial/connect/create_tables/tutorial001.py +++ b/docs_src/tutorial/connect/create_tables/tutorial001.py @@ -5,15 +5,15 @@ class Team(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) headquarters: str class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) team_id: Optional[int] = Field(default=None, foreign_key="team.id") diff --git a/docs_src/tutorial/connect/delete/tutorial001.py b/docs_src/tutorial/connect/delete/tutorial001.py index 57bbd0ee91..eeb376a0cc 100644 --- a/docs_src/tutorial/connect/delete/tutorial001.py +++ b/docs_src/tutorial/connect/delete/tutorial001.py @@ -5,15 +5,15 @@ class Team(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) headquarters: str class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) team_id: Optional[int] = Field(default=None, foreign_key="team.id") diff --git a/docs_src/tutorial/connect/insert/tutorial001.py b/docs_src/tutorial/connect/insert/tutorial001.py index d64d37f6a7..dc3661d7c7 100644 --- a/docs_src/tutorial/connect/insert/tutorial001.py +++ b/docs_src/tutorial/connect/insert/tutorial001.py @@ -5,15 +5,15 @@ class Team(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) headquarters: str class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) team_id: Optional[int] = Field(default=None, foreign_key="team.id") diff --git a/docs_src/tutorial/connect/select/tutorial001.py b/docs_src/tutorial/connect/select/tutorial001.py index 18c4f402d4..d4cdf413f1 100644 --- a/docs_src/tutorial/connect/select/tutorial001.py +++ b/docs_src/tutorial/connect/select/tutorial001.py @@ -5,15 +5,15 @@ class Team(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) headquarters: str class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) team_id: Optional[int] = Field(default=None, foreign_key="team.id") diff --git a/docs_src/tutorial/connect/select/tutorial002.py b/docs_src/tutorial/connect/select/tutorial002.py index f7df277d65..59edbf7fd9 100644 --- a/docs_src/tutorial/connect/select/tutorial002.py +++ b/docs_src/tutorial/connect/select/tutorial002.py @@ -5,15 +5,15 @@ class Team(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) headquarters: str class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) team_id: Optional[int] = Field(default=None, foreign_key="team.id") diff --git a/docs_src/tutorial/connect/select/tutorial003.py b/docs_src/tutorial/connect/select/tutorial003.py index 110cace997..fb5b8aa0c9 100644 --- a/docs_src/tutorial/connect/select/tutorial003.py +++ b/docs_src/tutorial/connect/select/tutorial003.py @@ -5,15 +5,15 @@ class Team(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) headquarters: str class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) team_id: Optional[int] = Field(default=None, foreign_key="team.id") diff --git a/docs_src/tutorial/connect/select/tutorial004.py b/docs_src/tutorial/connect/select/tutorial004.py index 87e739a91e..d1d260b3f4 100644 --- a/docs_src/tutorial/connect/select/tutorial004.py +++ b/docs_src/tutorial/connect/select/tutorial004.py @@ -5,15 +5,15 @@ class Team(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) headquarters: str class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) team_id: Optional[int] = Field(default=None, foreign_key="team.id") diff --git a/docs_src/tutorial/connect/select/tutorial005.py b/docs_src/tutorial/connect/select/tutorial005.py index 0e696d0382..a61ef8a015 100644 --- a/docs_src/tutorial/connect/select/tutorial005.py +++ b/docs_src/tutorial/connect/select/tutorial005.py @@ -5,15 +5,15 @@ class Team(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) headquarters: str class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) team_id: Optional[int] = Field(default=None, foreign_key="team.id") diff --git a/docs_src/tutorial/connect/update/tutorial001.py b/docs_src/tutorial/connect/update/tutorial001.py index 3c9726f9ef..0080340532 100644 --- a/docs_src/tutorial/connect/update/tutorial001.py +++ b/docs_src/tutorial/connect/update/tutorial001.py @@ -5,15 +5,15 @@ class Team(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) headquarters: str class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) team_id: Optional[int] = Field(default=None, foreign_key="team.id") diff --git a/docs_src/tutorial/delete/tutorial001.py b/docs_src/tutorial/delete/tutorial001.py index 0f7c056174..7c911df50e 100644 --- a/docs_src/tutorial/delete/tutorial001.py +++ b/docs_src/tutorial/delete/tutorial001.py @@ -5,9 +5,9 @@ class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) sqlite_file_name = "database.db" diff --git a/docs_src/tutorial/delete/tutorial002.py b/docs_src/tutorial/delete/tutorial002.py index 1f2671162e..202d63b6d3 100644 --- a/docs_src/tutorial/delete/tutorial002.py +++ b/docs_src/tutorial/delete/tutorial002.py @@ -5,9 +5,9 @@ class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) sqlite_file_name = "database.db" diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001/main.py b/docs_src/tutorial/fastapi/app_testing/tutorial001/main.py index 02f5ab8497..88b8fbbcea 100644 --- a/docs_src/tutorial/fastapi/app_testing/tutorial001/main.py +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001/main.py @@ -5,9 +5,9 @@ class HeroBase(SQLModel): - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) class Hero(HeroBase, table=True): diff --git a/docs_src/tutorial/fastapi/delete/tutorial001.py b/docs_src/tutorial/fastapi/delete/tutorial001.py index 19ab2bb3ca..3c15efbb2d 100644 --- a/docs_src/tutorial/fastapi/delete/tutorial001.py +++ b/docs_src/tutorial/fastapi/delete/tutorial001.py @@ -5,9 +5,9 @@ class HeroBase(SQLModel): - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) class Hero(HeroBase, table=True): diff --git a/docs_src/tutorial/fastapi/limit_and_offset/tutorial001.py b/docs_src/tutorial/fastapi/limit_and_offset/tutorial001.py index 9bdf60446a..aef21332a7 100644 --- a/docs_src/tutorial/fastapi/limit_and_offset/tutorial001.py +++ b/docs_src/tutorial/fastapi/limit_and_offset/tutorial001.py @@ -5,9 +5,9 @@ class HeroBase(SQLModel): - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) class Hero(HeroBase, table=True): diff --git a/docs_src/tutorial/fastapi/multiple_models/tutorial001.py b/docs_src/tutorial/fastapi/multiple_models/tutorial001.py index c46448be80..df20123333 100644 --- a/docs_src/tutorial/fastapi/multiple_models/tutorial001.py +++ b/docs_src/tutorial/fastapi/multiple_models/tutorial001.py @@ -6,9 +6,9 @@ class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) class HeroCreate(SQLModel): diff --git a/docs_src/tutorial/fastapi/multiple_models/tutorial002.py b/docs_src/tutorial/fastapi/multiple_models/tutorial002.py index 537e89637a..392c2c5829 100644 --- a/docs_src/tutorial/fastapi/multiple_models/tutorial002.py +++ b/docs_src/tutorial/fastapi/multiple_models/tutorial002.py @@ -5,9 +5,9 @@ class HeroBase(SQLModel): - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) class Hero(HeroBase, table=True): diff --git a/docs_src/tutorial/fastapi/read_one/tutorial001.py b/docs_src/tutorial/fastapi/read_one/tutorial001.py index bc83db5e04..4d66e471a5 100644 --- a/docs_src/tutorial/fastapi/read_one/tutorial001.py +++ b/docs_src/tutorial/fastapi/read_one/tutorial001.py @@ -5,9 +5,9 @@ class HeroBase(SQLModel): - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) class Hero(HeroBase, table=True): diff --git a/docs_src/tutorial/fastapi/relationships/tutorial001.py b/docs_src/tutorial/fastapi/relationships/tutorial001.py index da21e4686f..97220b95e5 100644 --- a/docs_src/tutorial/fastapi/relationships/tutorial001.py +++ b/docs_src/tutorial/fastapi/relationships/tutorial001.py @@ -5,7 +5,7 @@ class TeamBase(SQLModel): - name: str + name: str = Field(index=True) headquarters: str @@ -30,9 +30,9 @@ class TeamUpdate(SQLModel): class HeroBase(SQLModel): - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) team_id: Optional[int] = Field(default=None, foreign_key="team.id") diff --git a/docs_src/tutorial/fastapi/response_model/tutorial001.py b/docs_src/tutorial/fastapi/response_model/tutorial001.py index 5f7a19064e..57d8738395 100644 --- a/docs_src/tutorial/fastapi/response_model/tutorial001.py +++ b/docs_src/tutorial/fastapi/response_model/tutorial001.py @@ -6,9 +6,9 @@ class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) sqlite_file_name = "database.db" diff --git a/docs_src/tutorial/fastapi/session_with_dependency/tutorial001.py b/docs_src/tutorial/fastapi/session_with_dependency/tutorial001.py index 02f5ab8497..88b8fbbcea 100644 --- a/docs_src/tutorial/fastapi/session_with_dependency/tutorial001.py +++ b/docs_src/tutorial/fastapi/session_with_dependency/tutorial001.py @@ -5,9 +5,9 @@ class HeroBase(SQLModel): - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) class Hero(HeroBase, table=True): diff --git a/docs_src/tutorial/fastapi/simple_hero_api/tutorial001.py b/docs_src/tutorial/fastapi/simple_hero_api/tutorial001.py index 715836ce57..41eaa621d3 100644 --- a/docs_src/tutorial/fastapi/simple_hero_api/tutorial001.py +++ b/docs_src/tutorial/fastapi/simple_hero_api/tutorial001.py @@ -6,9 +6,9 @@ class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) sqlite_file_name = "database.db" diff --git a/docs_src/tutorial/fastapi/teams/tutorial001.py b/docs_src/tutorial/fastapi/teams/tutorial001.py index dc3e0939e2..e8f88b8e9e 100644 --- a/docs_src/tutorial/fastapi/teams/tutorial001.py +++ b/docs_src/tutorial/fastapi/teams/tutorial001.py @@ -5,7 +5,7 @@ class TeamBase(SQLModel): - name: str + name: str = Field(index=True) headquarters: str @@ -30,9 +30,9 @@ class TeamUpdate(SQLModel): class HeroBase(SQLModel): - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) team_id: Optional[int] = Field(default=None, foreign_key="team.id") diff --git a/docs_src/tutorial/fastapi/update/tutorial001.py b/docs_src/tutorial/fastapi/update/tutorial001.py index 9309d626b7..35554878db 100644 --- a/docs_src/tutorial/fastapi/update/tutorial001.py +++ b/docs_src/tutorial/fastapi/update/tutorial001.py @@ -5,9 +5,9 @@ class HeroBase(SQLModel): - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) class Hero(HeroBase, table=True): diff --git a/docs_src/tutorial/many_to_many/tutorial001.py b/docs_src/tutorial/many_to_many/tutorial001.py index aee77af3b1..bb4e9d0896 100644 --- a/docs_src/tutorial/many_to_many/tutorial001.py +++ b/docs_src/tutorial/many_to_many/tutorial001.py @@ -14,7 +14,7 @@ class HeroTeamLink(SQLModel, table=True): class Team(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) headquarters: str heroes: List["Hero"] = Relationship(back_populates="teams", link_model=HeroTeamLink) @@ -22,9 +22,9 @@ class Team(SQLModel, table=True): class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) teams: List[Team] = Relationship(back_populates="heroes", link_model=HeroTeamLink) diff --git a/docs_src/tutorial/many_to_many/tutorial002.py b/docs_src/tutorial/many_to_many/tutorial002.py index 123fa5a523..dc4aa0b770 100644 --- a/docs_src/tutorial/many_to_many/tutorial002.py +++ b/docs_src/tutorial/many_to_many/tutorial002.py @@ -14,7 +14,7 @@ class HeroTeamLink(SQLModel, table=True): class Team(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) headquarters: str heroes: List["Hero"] = Relationship(back_populates="teams", link_model=HeroTeamLink) @@ -22,9 +22,9 @@ class Team(SQLModel, table=True): class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) teams: List[Team] = Relationship(back_populates="heroes", link_model=HeroTeamLink) diff --git a/docs_src/tutorial/many_to_many/tutorial003.py b/docs_src/tutorial/many_to_many/tutorial003.py index c2f3d56d33..1e03c4af89 100644 --- a/docs_src/tutorial/many_to_many/tutorial003.py +++ b/docs_src/tutorial/many_to_many/tutorial003.py @@ -18,7 +18,7 @@ class HeroTeamLink(SQLModel, table=True): class Team(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) headquarters: str hero_links: List[HeroTeamLink] = Relationship(back_populates="team") @@ -26,9 +26,9 @@ class Team(SQLModel, table=True): class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) team_links: List[HeroTeamLink] = Relationship(back_populates="hero") diff --git a/docs_src/tutorial/offset_and_limit/tutorial001.py b/docs_src/tutorial/offset_and_limit/tutorial001.py index 5413c17ea3..1b033accb9 100644 --- a/docs_src/tutorial/offset_and_limit/tutorial001.py +++ b/docs_src/tutorial/offset_and_limit/tutorial001.py @@ -5,9 +5,9 @@ class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) sqlite_file_name = "database.db" diff --git a/docs_src/tutorial/offset_and_limit/tutorial002.py b/docs_src/tutorial/offset_and_limit/tutorial002.py index 9d85a1342b..65a62369f4 100644 --- a/docs_src/tutorial/offset_and_limit/tutorial002.py +++ b/docs_src/tutorial/offset_and_limit/tutorial002.py @@ -5,9 +5,9 @@ class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) sqlite_file_name = "database.db" diff --git a/docs_src/tutorial/offset_and_limit/tutorial003.py b/docs_src/tutorial/offset_and_limit/tutorial003.py index 5d2c3bffca..36cae9c42c 100644 --- a/docs_src/tutorial/offset_and_limit/tutorial003.py +++ b/docs_src/tutorial/offset_and_limit/tutorial003.py @@ -5,9 +5,9 @@ class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) sqlite_file_name = "database.db" diff --git a/docs_src/tutorial/offset_and_limit/tutorial004.py b/docs_src/tutorial/offset_and_limit/tutorial004.py index bfa882d352..a95715cd98 100644 --- a/docs_src/tutorial/offset_and_limit/tutorial004.py +++ b/docs_src/tutorial/offset_and_limit/tutorial004.py @@ -5,9 +5,9 @@ class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) sqlite_file_name = "database.db" diff --git a/docs_src/tutorial/one/tutorial001.py b/docs_src/tutorial/one/tutorial001.py index 9fa5f0b503..072f4a3bf5 100644 --- a/docs_src/tutorial/one/tutorial001.py +++ b/docs_src/tutorial/one/tutorial001.py @@ -5,9 +5,9 @@ class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) sqlite_file_name = "database.db" diff --git a/docs_src/tutorial/one/tutorial002.py b/docs_src/tutorial/one/tutorial002.py index a1d86e0bbf..c24490659f 100644 --- a/docs_src/tutorial/one/tutorial002.py +++ b/docs_src/tutorial/one/tutorial002.py @@ -5,9 +5,9 @@ class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) sqlite_file_name = "database.db" diff --git a/docs_src/tutorial/one/tutorial003.py b/docs_src/tutorial/one/tutorial003.py index fe8c05cf50..f8fb0bcd7d 100644 --- a/docs_src/tutorial/one/tutorial003.py +++ b/docs_src/tutorial/one/tutorial003.py @@ -5,9 +5,9 @@ class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) sqlite_file_name = "database.db" diff --git a/docs_src/tutorial/one/tutorial004.py b/docs_src/tutorial/one/tutorial004.py index 32bc9b9efc..8688fc4799 100644 --- a/docs_src/tutorial/one/tutorial004.py +++ b/docs_src/tutorial/one/tutorial004.py @@ -5,9 +5,9 @@ class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) sqlite_file_name = "database.db" diff --git a/docs_src/tutorial/one/tutorial005.py b/docs_src/tutorial/one/tutorial005.py index 238213670e..f624d4cb68 100644 --- a/docs_src/tutorial/one/tutorial005.py +++ b/docs_src/tutorial/one/tutorial005.py @@ -5,9 +5,9 @@ class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) sqlite_file_name = "database.db" diff --git a/docs_src/tutorial/one/tutorial006.py b/docs_src/tutorial/one/tutorial006.py index cf408c494a..afc48547d9 100644 --- a/docs_src/tutorial/one/tutorial006.py +++ b/docs_src/tutorial/one/tutorial006.py @@ -5,9 +5,9 @@ class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) sqlite_file_name = "database.db" diff --git a/docs_src/tutorial/one/tutorial007.py b/docs_src/tutorial/one/tutorial007.py index 8a36d978a4..15df5baa94 100644 --- a/docs_src/tutorial/one/tutorial007.py +++ b/docs_src/tutorial/one/tutorial007.py @@ -5,9 +5,9 @@ class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) sqlite_file_name = "database.db" diff --git a/docs_src/tutorial/one/tutorial008.py b/docs_src/tutorial/one/tutorial008.py index 1b0d6ef501..9aa077ca38 100644 --- a/docs_src/tutorial/one/tutorial008.py +++ b/docs_src/tutorial/one/tutorial008.py @@ -5,9 +5,9 @@ class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) sqlite_file_name = "database.db" diff --git a/docs_src/tutorial/one/tutorial009.py b/docs_src/tutorial/one/tutorial009.py index 70deed28a8..f4ee23b355 100644 --- a/docs_src/tutorial/one/tutorial009.py +++ b/docs_src/tutorial/one/tutorial009.py @@ -5,9 +5,9 @@ class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) sqlite_file_name = "database.db" diff --git a/docs_src/tutorial/relationship_attributes/back_populates/tutorial001.py b/docs_src/tutorial/relationship_attributes/back_populates/tutorial001.py index d9851b4e43..fc4eb97934 100644 --- a/docs_src/tutorial/relationship_attributes/back_populates/tutorial001.py +++ b/docs_src/tutorial/relationship_attributes/back_populates/tutorial001.py @@ -5,7 +5,7 @@ class Team(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) headquarters: str heroes: List["Hero"] = Relationship() @@ -13,9 +13,9 @@ class Team(SQLModel, table=True): class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) team_id: Optional[int] = Field(default=None, foreign_key="team.id") team: Optional[Team] = Relationship() diff --git a/docs_src/tutorial/relationship_attributes/back_populates/tutorial002.py b/docs_src/tutorial/relationship_attributes/back_populates/tutorial002.py index b33fabe716..a25df4e75d 100644 --- a/docs_src/tutorial/relationship_attributes/back_populates/tutorial002.py +++ b/docs_src/tutorial/relationship_attributes/back_populates/tutorial002.py @@ -5,7 +5,7 @@ class Team(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) headquarters: str heroes: List["Hero"] = Relationship(back_populates="team") @@ -13,9 +13,9 @@ class Team(SQLModel, table=True): class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) team_id: Optional[int] = Field(default=None, foreign_key="team.id") team: Optional[Team] = Relationship(back_populates="heroes") diff --git a/docs_src/tutorial/relationship_attributes/back_populates/tutorial003.py b/docs_src/tutorial/relationship_attributes/back_populates/tutorial003.py index cbd1581b10..c137f58f6a 100644 --- a/docs_src/tutorial/relationship_attributes/back_populates/tutorial003.py +++ b/docs_src/tutorial/relationship_attributes/back_populates/tutorial003.py @@ -5,14 +5,14 @@ class Weapon(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) hero: "Hero" = Relationship(back_populates="weapon") class Power(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) hero_id: int = Field(foreign_key="hero.id") hero: "Hero" = Relationship(back_populates="powers") @@ -20,7 +20,7 @@ class Power(SQLModel, table=True): class Team(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) headquarters: str heroes: List["Hero"] = Relationship(back_populates="team") @@ -28,9 +28,9 @@ class Team(SQLModel, table=True): class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) team_id: Optional[int] = Field(default=None, foreign_key="team.id") team: Optional[Team] = Relationship(back_populates="heroes") diff --git a/docs_src/tutorial/relationship_attributes/create_and_update_relationships/tutorial001.py b/docs_src/tutorial/relationship_attributes/create_and_update_relationships/tutorial001.py index 2bf2041ff9..ec9c909d73 100644 --- a/docs_src/tutorial/relationship_attributes/create_and_update_relationships/tutorial001.py +++ b/docs_src/tutorial/relationship_attributes/create_and_update_relationships/tutorial001.py @@ -5,7 +5,7 @@ class Team(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) headquarters: str heroes: List["Hero"] = Relationship(back_populates="team") @@ -13,9 +13,9 @@ class Team(SQLModel, table=True): class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) team_id: Optional[int] = Field(default=None, foreign_key="team.id") team: Optional[Team] = Relationship(back_populates="heroes") diff --git a/docs_src/tutorial/relationship_attributes/define_relationship_attributes/tutorial001.py b/docs_src/tutorial/relationship_attributes/define_relationship_attributes/tutorial001.py index 98c1919209..71cb3f6136 100644 --- a/docs_src/tutorial/relationship_attributes/define_relationship_attributes/tutorial001.py +++ b/docs_src/tutorial/relationship_attributes/define_relationship_attributes/tutorial001.py @@ -5,7 +5,7 @@ class Team(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) headquarters: str heroes: List["Hero"] = Relationship(back_populates="team") @@ -13,9 +13,9 @@ class Team(SQLModel, table=True): class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) team_id: Optional[int] = Field(default=None, foreign_key="team.id") team: Optional[Team] = Relationship(back_populates="heroes") diff --git a/docs_src/tutorial/relationship_attributes/read_relationships/tutorial001.py b/docs_src/tutorial/relationship_attributes/read_relationships/tutorial001.py index e5c23a7264..5f718cab45 100644 --- a/docs_src/tutorial/relationship_attributes/read_relationships/tutorial001.py +++ b/docs_src/tutorial/relationship_attributes/read_relationships/tutorial001.py @@ -5,7 +5,7 @@ class Team(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) headquarters: str heroes: List["Hero"] = Relationship(back_populates="team") @@ -13,9 +13,9 @@ class Team(SQLModel, table=True): class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) team_id: Optional[int] = Field(default=None, foreign_key="team.id") team: Optional[Team] = Relationship(back_populates="heroes") diff --git a/docs_src/tutorial/relationship_attributes/read_relationships/tutorial002.py b/docs_src/tutorial/relationship_attributes/read_relationships/tutorial002.py index efae8e556c..fdb436eb5f 100644 --- a/docs_src/tutorial/relationship_attributes/read_relationships/tutorial002.py +++ b/docs_src/tutorial/relationship_attributes/read_relationships/tutorial002.py @@ -5,7 +5,7 @@ class Team(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) headquarters: str heroes: List["Hero"] = Relationship(back_populates="team") @@ -13,9 +13,9 @@ class Team(SQLModel, table=True): class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) team_id: Optional[int] = Field(default=None, foreign_key="team.id") team: Optional[Team] = Relationship(back_populates="heroes") diff --git a/docs_src/tutorial/update/tutorial001.py b/docs_src/tutorial/update/tutorial001.py index 96c72088fa..37868acc96 100644 --- a/docs_src/tutorial/update/tutorial001.py +++ b/docs_src/tutorial/update/tutorial001.py @@ -5,9 +5,9 @@ class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) sqlite_file_name = "database.db" diff --git a/docs_src/tutorial/update/tutorial002.py b/docs_src/tutorial/update/tutorial002.py index 04185f8e76..4880f73f90 100644 --- a/docs_src/tutorial/update/tutorial002.py +++ b/docs_src/tutorial/update/tutorial002.py @@ -5,9 +5,9 @@ class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) sqlite_file_name = "database.db" diff --git a/docs_src/tutorial/update/tutorial003.py b/docs_src/tutorial/update/tutorial003.py index c3199152bd..fd2ed75f0b 100644 --- a/docs_src/tutorial/update/tutorial003.py +++ b/docs_src/tutorial/update/tutorial003.py @@ -5,9 +5,9 @@ class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) sqlite_file_name = "database.db" diff --git a/docs_src/tutorial/update/tutorial004.py b/docs_src/tutorial/update/tutorial004.py index e61a04fbec..868c66c7d4 100644 --- a/docs_src/tutorial/update/tutorial004.py +++ b/docs_src/tutorial/update/tutorial004.py @@ -5,9 +5,9 @@ class Hero(SQLModel, table=True): id: Optional[int] = Field(default=None, primary_key=True) - name: str + name: str = Field(index=True) secret_name: str - age: Optional[int] = None + age: Optional[int] = Field(default=None, index=True) sqlite_file_name = "database.db" From 3778c03c50623106a92809ff1d3cd27e379117f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Tue, 28 Dec 2021 11:05:04 +0100 Subject: [PATCH 7/9] =?UTF-8?q?=F0=9F=93=9D=20Update=20docs=20with=20refer?= =?UTF-8?q?ences=20to=20indexes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../connect/create-connected-tables.md | 1 + docs/tutorial/fastapi/multiple-models.md | 25 +++++++++++++++++++ docs/tutorial/one.md | 6 ++--- docs/tutorial/update.md | 2 +- docs/tutorial/where.md | 8 ++++-- 5 files changed, 36 insertions(+), 6 deletions(-) diff --git a/docs/tutorial/connect/create-connected-tables.md b/docs/tutorial/connect/create-connected-tables.md index 90301ee41f..452c904ebe 100644 --- a/docs/tutorial/connect/create-connected-tables.md +++ b/docs/tutorial/connect/create-connected-tables.md @@ -78,6 +78,7 @@ The `Team` model will be in a table automatically named `"team"`, and it will ha * `id`, the primary key, automatically generated by the database * `name`, the name of the team + * We also tell **SQLModel** to create an index for this column * `headquarters`, the headquarters of the team And finally we mark it as a table in the config. diff --git a/docs/tutorial/fastapi/multiple-models.md b/docs/tutorial/fastapi/multiple-models.md index 5cac6dda91..d313874c98 100644 --- a/docs/tutorial/fastapi/multiple-models.md +++ b/docs/tutorial/fastapi/multiple-models.md @@ -305,6 +305,31 @@ And of course, all these fields will be in the columns for the resulting `hero` And those inherited fields will also be in the **autocompletion** and **inline errors** in editors, etc. +### Columns and Inheritance with Multiple Models + +Notice that the parent model `HeroBase` is not a **table model**, but still, we can declare `name` and `age` using `Field(index=True)`. + +```Python hl_lines="4 6 9" +# Code above omitted 👆 + +{!./docs_src/tutorial/fastapi/multiple_models/tutorial002.py[ln:7-14]!} + +# Code below omitted 👇 +``` + +
+👀 Full file preview + +```Python +{!./docs_src/tutorial/fastapi/multiple_models/tutorial002.py!} +``` + +
+ +This won't affect this parent **data model** `HeroBase`. + +But once the child model `Hero` (the actual **table model**) inherits those fields, it will use those field configurations to create the indexes when creating the tables in the database. + ### The `HeroCreate` **Data Model** Now let's see the `HeroCreate` model that will be used to define the data that we want to receive in the API when creating a new hero. diff --git a/docs/tutorial/one.md b/docs/tutorial/one.md index 901199bc42..3b60653ed9 100644 --- a/docs/tutorial/one.md +++ b/docs/tutorial/one.md @@ -18,7 +18,7 @@ We'll continue with the same examples we have been using in the previous chapter 👀 Full file preview ```Python -{!./docs_src/tutorial/where/tutorial006.py!} +{!./docs_src/tutorial/indexes/tutorial002.py!} ``` @@ -32,7 +32,7 @@ We have been iterating over the rows in a `result` object like: ```Python hl_lines="7-8" # Code above omitted 👆 -{!./docs_src/tutorial/where/tutorial006.py[ln:44-49]!} +{!./docs_src/tutorial/indexes/tutorial002.py[ln:44-49]!} # Code below omitted 👇 ``` @@ -41,7 +41,7 @@ We have been iterating over the rows in a `result` object like: 👀 Full file preview ```Python -{!./docs_src/tutorial/where/tutorial006.py!} +{!./docs_src/tutorial/indexes/tutorial002.py!} ``` diff --git a/docs/tutorial/update.md b/docs/tutorial/update.md index 420616d78a..b3099f5a16 100644 --- a/docs/tutorial/update.md +++ b/docs/tutorial/update.md @@ -10,7 +10,7 @@ As before, we'll continue from where we left off with the previous code. 👀 Full file preview ```Python -{!./docs_src/tutorial/where/tutorial006.py!} +{!./docs_src/tutorial/indexes/tutorial002.py!} ``` diff --git a/docs/tutorial/where.md b/docs/tutorial/where.md index fd807127cc..45e909cc75 100644 --- a/docs/tutorial/where.md +++ b/docs/tutorial/where.md @@ -233,7 +233,7 @@ The object returned by `select(Hero)` is a special type of object with some meth One of those methods is `.where()` used to (unsurprisingly) add a `WHERE` to the SQL statement in that **select** object. -There are other methods that will will explore later. 💡 +There are other methods that we will explore later. 💡 Most of these methods return the same object again after modifying it. @@ -698,7 +698,7 @@ age=35 id=5 name='Black Lion' secret_name='Trevor Challa' Here's a good moment to see that being able to use these pure Python expressions instead of keyword arguments can help a lot. ✨ -We can use the same standard Python comparison operators like `. +We can use the same standard Python comparison operators like `<`, `<=`, `>`, `>=`, `==`, etc. ## Multiple `.where()` @@ -933,3 +933,7 @@ And with that the editor knows this code is actually fine, because this is a spe ## Recap You can use `.where()` with powerful expressions using **SQLModel** columns (the special class attributes) to filter the rows that you want. 🚀 + +Up to now, the database would have been **looking through each one of the records** (rows) to find the ones that match what you want. If you have thousands or millions of records, this could be very **slow**. 😱 + +In the next section I'll tell you how to add **indexes** to the database, this is what will make the queries **very efficient**. 😎 From 6715817fed5efda9f727c93723561f1f238bb687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Tue, 28 Dec 2021 11:06:15 +0100 Subject: [PATCH 8/9] =?UTF-8?q?=E2=9C=85=20Update=20tests=20with=20indexes?= =?UTF-8?q?=20and=20the=20variability=20in=20results?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test_multiple_models/test_tutorial001.py | 15 +++++++++++++++ .../test_multiple_models/test_tutorial002.py | 15 +++++++++++++++ .../test_indexes/test_tutorial001.py | 7 ++++++- .../test_indexes/test_tutorial006.py | 7 ++++++- .../test_tutorial/test_where/test_tutorial003.py | 7 ++++++- .../test_tutorial/test_where/test_tutorial004.py | 7 ++++++- .../test_tutorial/test_where/test_tutorial011.py | 7 ++++++- 7 files changed, 60 insertions(+), 5 deletions(-) diff --git a/tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial001.py b/tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial001.py index ac85eca2d5..cf008563f4 100644 --- a/tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial001.py +++ b/tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial001.py @@ -1,4 +1,6 @@ from fastapi.testclient import TestClient +from sqlalchemy import inspect +from sqlalchemy.engine.reflection import Inspector from sqlmodel import create_engine from sqlmodel.pool import StaticPool @@ -166,3 +168,16 @@ def test_tutorial(clear_sqlmodel): assert response.status_code == 200, response.text assert data == openapi_schema + + # Test inherited indexes + insp: Inspector = inspect(mod.engine) + indexes = insp.get_indexes(str(mod.Hero.__tablename__)) + expected_indexes = [ + {"name": "ix_hero_name", "column_names": ["name"], "unique": 0}, + {"name": "ix_hero_age", "column_names": ["age"], "unique": 0}, + ] + for index in expected_indexes: + assert index in indexes, "This expected index should be in the indexes in DB" + # Now that this index was checked, remove it from the list of indexes + indexes.pop(indexes.index(index)) + assert len(indexes) == 0, "The database should only have the expected indexes" diff --git a/tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial002.py b/tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial002.py index 421a1cad53..57393a7ddc 100644 --- a/tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial002.py +++ b/tests/test_tutorial/test_fastapi/test_multiple_models/test_tutorial002.py @@ -1,4 +1,6 @@ from fastapi.testclient import TestClient +from sqlalchemy import inspect +from sqlalchemy.engine.reflection import Inspector from sqlmodel import create_engine from sqlmodel.pool import StaticPool @@ -166,3 +168,16 @@ def test_tutorial(clear_sqlmodel): assert response.status_code == 200, response.text assert data == openapi_schema + + # Test inherited indexes + insp: Inspector = inspect(mod.engine) + indexes = insp.get_indexes(str(mod.Hero.__tablename__)) + expected_indexes = [ + {"name": "ix_hero_age", "column_names": ["age"], "unique": 0}, + {"name": "ix_hero_name", "column_names": ["name"], "unique": 0}, + ] + for index in expected_indexes: + assert index in indexes, "This expected index should be in the indexes in DB" + # Now that this index was checked, remove it from the list of indexes + indexes.pop(indexes.index(index)) + assert len(indexes) == 0, "The database should only have the expected indexes" diff --git a/tests/test_tutorial/test_indexes/test_tutorial001.py b/tests/test_tutorial/test_indexes/test_tutorial001.py index 0c3fcf01ba..596207737d 100644 --- a/tests/test_tutorial/test_indexes/test_tutorial001.py +++ b/tests/test_tutorial/test_indexes/test_tutorial001.py @@ -24,7 +24,12 @@ def test_tutorial(clear_sqlmodel): insp: Inspector = inspect(mod.engine) indexes = insp.get_indexes(str(mod.Hero.__tablename__)) - assert indexes == [ + expected_indexes = [ {"name": "ix_hero_name", "column_names": ["name"], "unique": 0}, {"name": "ix_hero_age", "column_names": ["age"], "unique": 0}, ] + for index in expected_indexes: + assert index in indexes, "This expected index should be in the indexes in DB" + # Now that this index was checked, remove it from the list of indexes + indexes.pop(indexes.index(index)) + assert len(indexes) == 0, "The database should only have the expected indexes" diff --git a/tests/test_tutorial/test_indexes/test_tutorial006.py b/tests/test_tutorial/test_indexes/test_tutorial006.py index 1e81c606bf..e26f8b2ed8 100644 --- a/tests/test_tutorial/test_indexes/test_tutorial006.py +++ b/tests/test_tutorial/test_indexes/test_tutorial006.py @@ -25,7 +25,12 @@ def test_tutorial(clear_sqlmodel): insp: Inspector = inspect(mod.engine) indexes = insp.get_indexes(str(mod.Hero.__tablename__)) - assert indexes == [ + expected_indexes = [ {"name": "ix_hero_name", "column_names": ["name"], "unique": 0}, {"name": "ix_hero_age", "column_names": ["age"], "unique": 0}, ] + for index in expected_indexes: + assert index in indexes, "This expected index should be in the indexes in DB" + # Now that this index was checked, remove it from the list of indexes + indexes.pop(indexes.index(index)) + assert len(indexes) == 0, "The database should only have the expected indexes" diff --git a/tests/test_tutorial/test_where/test_tutorial003.py b/tests/test_tutorial/test_where/test_tutorial003.py index a01955e6b7..4794d846ff 100644 --- a/tests/test_tutorial/test_where/test_tutorial003.py +++ b/tests/test_tutorial/test_where/test_tutorial003.py @@ -17,7 +17,7 @@ def test_tutorial(clear_sqlmodel): with patch("builtins.print", new=new_print): mod.main() - assert calls == [ + expected_calls = [ [{"id": 6, "name": "Dr. Weird", "secret_name": "Steve Weird", "age": 36}], [{"id": 3, "name": "Rusty-Man", "secret_name": "Tommy Sharp", "age": 48}], [ @@ -29,3 +29,8 @@ def test_tutorial(clear_sqlmodel): } ], ] + for call in expected_calls: + assert call in calls, "This expected item should be in the list" + # Now that this item was checked, remove it from the list + calls.pop(calls.index(call)) + assert len(calls) == 0, "The list should only have the expected items" diff --git a/tests/test_tutorial/test_where/test_tutorial004.py b/tests/test_tutorial/test_where/test_tutorial004.py index 9f4f80c201..682babd43a 100644 --- a/tests/test_tutorial/test_where/test_tutorial004.py +++ b/tests/test_tutorial/test_where/test_tutorial004.py @@ -16,7 +16,7 @@ def test_tutorial(clear_sqlmodel): with patch("builtins.print", new=new_print): mod.main() - assert calls == [ + expected_calls = [ [{"id": 5, "name": "Black Lion", "secret_name": "Trevor Challa", "age": 35}], [{"id": 6, "name": "Dr. Weird", "secret_name": "Steve Weird", "age": 36}], [{"id": 3, "name": "Rusty-Man", "secret_name": "Tommy Sharp", "age": 48}], @@ -29,3 +29,8 @@ def test_tutorial(clear_sqlmodel): } ], ] + for call in expected_calls: + assert call in calls, "This expected item should be in the list" + # Now that this item was checked, remove it from the list + calls.pop(calls.index(call)) + assert len(calls) == 0, "The list should only have the expected items" diff --git a/tests/test_tutorial/test_where/test_tutorial011.py b/tests/test_tutorial/test_where/test_tutorial011.py index 743ecd5402..8006cd0708 100644 --- a/tests/test_tutorial/test_where/test_tutorial011.py +++ b/tests/test_tutorial/test_where/test_tutorial011.py @@ -16,7 +16,7 @@ def test_tutorial(clear_sqlmodel): with patch("builtins.print", new=new_print): mod.main() - assert calls == [ + expected_calls = [ [{"id": 5, "name": "Black Lion", "secret_name": "Trevor Challa", "age": 35}], [{"id": 6, "name": "Dr. Weird", "secret_name": "Steve Weird", "age": 36}], [{"id": 3, "name": "Rusty-Man", "secret_name": "Tommy Sharp", "age": 48}], @@ -29,3 +29,8 @@ def test_tutorial(clear_sqlmodel): } ], ] + for call in expected_calls: + assert call in calls, "This expected item should be in the list" + # Now that this item was checked, remove it from the list + calls.pop(calls.index(call)) + assert len(calls) == 0, "The list should only have the expected items" From dd453baa2754f95098bd30a5d8734e7be52d0c74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Tue, 28 Dec 2021 11:07:19 +0100 Subject: [PATCH 9/9] =?UTF-8?q?=F0=9F=94=A7=20Update=20MkDocs=20to=20inclu?= =?UTF-8?q?de=20indexes=20and=20fine-tune=20configs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mkdocs.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 41b44b6931..13744db8fd 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -19,6 +19,7 @@ theme: features: - search.suggest - search.highlight + - content.tabs.link icon: repo: fontawesome/brands/github-alt logo: img/icon-white.svg @@ -43,6 +44,7 @@ nav: - tutorial/automatic-id-none-refresh.md - tutorial/select.md - tutorial/where.md + - tutorial/indexes.md - tutorial/one.md - tutorial/limit-and-offset.md - tutorial/update.md @@ -103,7 +105,8 @@ markdown_extensions: - name: mermaid class: mermaid format: !!python/name:pymdownx.superfences.fence_div_format '' -- pymdownx.tabbed +- pymdownx.tabbed: + alternate_style: true - mdx_include extra: