]> git.ipfire.org Git - thirdparty/fastapi/sqlmodel.git/commitdiff
🔧 Update config with new pymdown extensions (#712)
authorSebastián Ramírez <tiangolo@gmail.com>
Tue, 28 Nov 2023 20:50:33 +0000 (21:50 +0100)
committerGitHub <noreply@github.com>
Tue, 28 Nov 2023 20:50:33 +0000 (21:50 +0100)
* 🔧 Update config with new pymdown extensions

* 📝 Update admonition blocks syntax

* 📝 Update syntax for tabs with new pymdown extensions

39 files changed:
docs/advanced/decimal.md
docs/databases.md
docs/db-to-code.md
docs/features.md
docs/help.md
docs/tutorial/automatic-id-none-refresh.md
docs/tutorial/code-structure.md
docs/tutorial/connect/create-connected-rows.md
docs/tutorial/connect/create-connected-tables.md
docs/tutorial/connect/index.md
docs/tutorial/connect/read-connected-data.md
docs/tutorial/create-db-and-table-with-db-browser.md
docs/tutorial/create-db-and-table.md
docs/tutorial/delete.md
docs/tutorial/fastapi/limit-and-offset.md
docs/tutorial/fastapi/multiple-models.md
docs/tutorial/fastapi/read-one.md
docs/tutorial/fastapi/response-model.md
docs/tutorial/fastapi/session-with-dependency.md
docs/tutorial/fastapi/simple-hero-api.md
docs/tutorial/fastapi/tests.md
docs/tutorial/fastapi/update.md
docs/tutorial/index.md
docs/tutorial/indexes.md
docs/tutorial/insert.md
docs/tutorial/limit-and-offset.md
docs/tutorial/many-to-many/index.md
docs/tutorial/many-to-many/link-with-extra-fields.md
docs/tutorial/many-to-many/update-remove-relationships.md
docs/tutorial/one.md
docs/tutorial/relationship-attributes/back-populates.md
docs/tutorial/relationship-attributes/define-relationships-attributes.md
docs/tutorial/relationship-attributes/index.md
docs/tutorial/relationship-attributes/read-relationships.md
docs/tutorial/relationship-attributes/type-annotation-strings.md
docs/tutorial/select.md
docs/tutorial/update.md
docs/tutorial/where.md
mkdocs.yml

index c0541b75df60dc10d6b9a2c25824b6ae9ef0e21a..3cd916399f7701f38b9107847d4f9754cbd6e78d 100644 (file)
@@ -21,15 +21,21 @@ In most cases this would probably not be a problem, for example measuring views
 
 Pydantic has special support for `Decimal` types using the <a href="https://pydantic-docs.helpmanual.io/usage/types/#arguments-to-condecimal" class="external-link" target="_blank">`condecimal()` special function</a>.
 
-!!! tip
-    Pydantic 1.9, that will be released soon, has improved support for `Decimal` types, without needing to use the `condecimal()` function.
+/// tip
 
-    But meanwhile, you can already use this feature with `condecimal()` in **SQLModel** it as it's explained here.
+Pydantic 1.9, that will be released soon, has improved support for `Decimal` types, without needing to use the `condecimal()` function.
+
+But meanwhile, you can already use this feature with `condecimal()` in **SQLModel** it as it's explained here.
+
+///
 
 When you use `condecimal()` you can specify the number of digits and decimal places to support. They will be validated by Pydantic (for example when using FastAPI) and the same information will also be used for the database columns.
 
-!!! info
-    For the database, **SQLModel** will use <a href="https://docs.sqlalchemy.org/en/14/core/type_basics.html#sqlalchemy.types.DECIMAL" class="external-link" target="_blank">SQLAlchemy's `DECIMAL` type</a>.
+/// info
+
+For the database, **SQLModel** will use <a href="https://docs.sqlalchemy.org/en/14/core/type_basics.html#sqlalchemy.types.DECIMAL" class="external-link" target="_blank">SQLAlchemy's `DECIMAL` type</a>.
+
+///
 
 ## Decimals in SQLModel
 
@@ -72,8 +78,11 @@ We are also saying that the number of decimal places (to the right of the decima
 * `123`
     * Even though this number doesn't have any decimals, we still have 3 places saved for them, which means that we can **only use 2 places** for the **integer part**, and this number has 3 integer digits. So, the allowed number of integer digits is `max_digits` - `decimal_places` = 2.
 
-!!! tip
-    Make sure you adjust the number of digits and decimal places for your own needs, in your own application. 🤓
+/// tip
+
+Make sure you adjust the number of digits and decimal places for your own needs, in your own application. 🤓
+
+///
 
 ## Create models with Decimals
 
@@ -142,7 +151,10 @@ Total money: 3.300
 
 </div>
 
-!!! warning
-    Although Decimal types are supported and used in the Python side, not all databases support it. In particular, SQLite doesn't support decimals, so it will convert them to the same floating `NUMERIC` type it supports.
+/// warning
+
+Although Decimal types are supported and used in the Python side, not all databases support it. In particular, SQLite doesn't support decimals, so it will convert them to the same floating `NUMERIC` type it supports.
+
+But decimals are supported by most of the other SQL databases. 🎉
 
-    But decimals are supported by most of the other SQL databases. 🎉
+///
index f1aaf663ab9709760ff703f8096966ec704c0fb6..54d5e4b18ed8e0d9bf1464d04d1a823a4cfb6b6c 100644 (file)
@@ -1,9 +1,12 @@
 # Intro to Databases
 
-!!! info
-    Are you a seasoned developer and already know everything about databases? 🤓
+/// info
 
-    Then you can skip to the [Tutorial - User Guide: First Steps](tutorial/index.md){.internal-link target=_blank} right away.
+Are you a seasoned developer and already know everything about databases? 🤓
+
+Then you can skip to the [Tutorial - User Guide: First Steps](tutorial/index.md){.internal-link target=_blank} right away.
+
+///
 
 If you don't know everything about databases, here's a quick overview.
 
@@ -17,8 +20,11 @@ So, what is a database?
 
 A **database** is a system to store and manage data in a structured and very efficient way.
 
-!!! tip
-    It's very common to abbreviate the word "database" as **"DB"**.
+/// tip
+
+It's very common to abbreviate the word "database" as **"DB"**.
+
+///
 
 As there's a lot of information about databases, and it can get very technical and academic, I'll give you a quick overview about some of the main concepts here.
 
@@ -28,8 +34,11 @@ I'll even tell you a bit about different types of databases, including the ones
 
 When starting to program, it might **not be obvious** why having a database apart from the code for your program is a **good idea**. Let's start with that.
 
-!!! tip
-    If that's obvious to you, just continue in the next section below. 👇
+/// tip
+
+If that's obvious to you, just continue in the next section below. 👇
+
+///
 
 In your code you already have **variables**, **dictionaries**, **lists**, etc. They all store **data** in some way already. Why would you need to have a separate database?
 
@@ -308,8 +317,11 @@ Next, it receives the data and puts it in Python objects that you can continue t
 
 I'll tell you more about SQL, SQLModel, how to use them, and how they are related in the next sections.
 
-!!! info "Technical Details"
-    SQLModel is built on top of SQLAlchemy. It is, in fact, just <a href="https://www.sqlalchemy.org/" class="external-link" target="_blank">SQLAlchemy</a> and <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> mixed together with some sugar on top.
+/// info  | Technical Details
+
+SQLModel is built on top of SQLAlchemy. It is, in fact, just <a href="https://www.sqlalchemy.org/" class="external-link" target="_blank">SQLAlchemy</a> and <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> mixed together with some sugar on top.
+
+///
 
 ## NoSQL Databases
 
index 980c457148e786d78bdae2baf7b7eebfe1c30ee1..537c583d3ff4a899c3e5fe3b4bf8b15e28964c47 100644 (file)
@@ -172,8 +172,11 @@ The difference in the final SQL statement is subtle, but it changes the meaning
 SELECT * FROM hero WHERE id = "2; DROP TABLE hero;";
 ```
 
-!!! tip
-    Notice the double quotes (`"`) making it a string instead of more raw SQL.
+/// tip
+
+Notice the double quotes (`"`) making it a string instead of more raw SQL.
+
+///
 
 The database will not find any record with that ID:
 
@@ -187,8 +190,11 @@ Then your code will continue to execute and calmly tell the user that it couldn'
 
 But we never deleted the `hero` table. 🎉
 
-!!! info
-    Of course, there are also other ways to do SQL data sanitization without using a tool like **SQLModel**, but it's still a nice feature you get by default.
+/// info
+
+Of course, there are also other ways to do SQL data sanitization without using a tool like **SQLModel**, but it's still a nice feature you get by default.
+
+///
 
 ### Editor Support
 
@@ -291,8 +297,11 @@ There are many ORMs available apart from **SQLModel**, you can read more about s
 
 ## SQL Table Names
 
-!!! info "Technical Background"
-    This is a bit of boring background for SQL purists. Feel free to skip this section. 😉
+/// info  | Technical Background
+
+This is a bit of boring background for SQL purists. Feel free to skip this section. 😉
+
+///
 
 When working with pure SQL, it's common to name the tables in plural. So, the table would be named `heroes` instead of `hero`, because it could contain multiple rows, each with one hero.
 
@@ -304,5 +313,8 @@ You will see **your own code** a lot more than the internal table names, so it's
 
 So, to keep things consistent, I'll keep using the same table names that **SQLModel** would have generated.
 
-!!! tip
-    You can also override the table name. You can read about it in the Advanced User Guide.
+/// tip
+
+You can also override the table name. You can read about it in the Advanced User Guide.
+
+///
index 102edef72541c73d6dfe69a6c6d0b6c8918c568b..f84606b9b5ed080597608d3c016c0be902a7b20f 100644 (file)
@@ -40,12 +40,15 @@ You won't need to keep guessing the types of different attributes in your models
 
 <img class="shadow" src="/img/index/autocompletion01.png">
 
-!!! info
-    Don't worry, adopting this in-development standard only affects/improves editor support.
+/// info
 
-    It doesn't affect performance or correctness. And if the in-progress standard was deprecated your code won't be affected.
+Don't worry, adopting this in-development standard only affects/improves editor support.
 
-    Meanwhile, you will get inline errors (like type checks) and autocompletion on places you wouldn't get with any other library. 🎉
+It doesn't affect performance or correctness. And if the in-progress standard was deprecated your code won't be affected.
+
+Meanwhile, you will get inline errors (like type checks) and autocompletion on places you wouldn't get with any other library. 🎉
+
+///
 
 ## Short
 
index d0d2308de294e19da2380c1fe978bc18c7356951..8dc524b23bd1a6ace66dfa3eaab0b9d69f23d252 100644 (file)
@@ -157,12 +157,15 @@ And if there's any other style or consistency need, I'll ask directly for that,
 
 * Then **comment** saying that you did that, that's how I will know you really checked it.
 
-!!! info
-    Unfortunately, I can't simply trust PRs that just have several approvals.
+/// info
 
-    Several times it has happened that there are PRs with 3, 5 or more approvals, probably because the description is appealing, but when I check the PRs, they are actually broken, have a bug, or don't solve the problem they claim to solve. 😅
+Unfortunately, I can't simply trust PRs that just have several approvals.
 
-    So, it's really important that you actually read and run the code, and let me know in the comments that you did. 🤓
+Several times it has happened that there are PRs with 3, 5 or more approvals, probably because the description is appealing, but when I check the PRs, they are actually broken, have a bug, or don't solve the problem they claim to solve. 😅
+
+So, it's really important that you actually read and run the code, and let me know in the comments that you did. 🤓
+
+///
 
 * If the PR can be simplified in a way, you can ask for that, but there's no need to be too picky, there might be a lot of subjective points of view (and I will have my own as well 🙈), so it's better if you can focus on the fundamental things.
 
@@ -209,10 +212,13 @@ If you can help me with that, **you are helping me maintain SQLModel** and makin
 
 Join the 👥 <a href="https://discord.gg/VQjSZaeJmf" class="external-link" target="_blank">FastAPI and Friends Discord chat server</a> 👥 and hang out with others in the community. There's a `#sqlmodel` channel.
 
-!!! tip
-    For questions, ask them in <a href="https://github.com/tiangolo/sqlmodel/discussions/new?category=questions" class="external-link" target="_blank">GitHub Discussions</a>, there's a much better chance you will receive help there.
+/// tip
+
+For questions, ask them in <a href="https://github.com/tiangolo/sqlmodel/discussions/new?category=questions" class="external-link" target="_blank">GitHub Discussions</a>, there's a much better chance you will receive help there.
+
+Use the chat only for other general conversations.
 
-    Use the chat only for other general conversations.
+///
 
 ### Don't use the chat for questions
 
index c7cf975ad49b823360ccf5da22ce24455a655a11..e9ac7fdf0a01f77b2256337c0fe08b05053be79d 100644 (file)
@@ -445,10 +445,13 @@ Hero 3: age=48 id=3 name='Rusty-Man' secret_name='Tommy Sharp'
 
 Now let's review all this code once again.
 
-!!! tip
-    Each one of the numbered bubbles shows what each line will print in the output.
+/// tip
 
-    And as we created the **engine** with `echo=True`, we can see the SQL statements being executed at each step.
+Each one of the numbered bubbles shows what each line will print in the output.
+
+And as we created the **engine** with `echo=True`, we can see the SQL statements being executed at each step.
+
+///
 
 ```{ .python .annotate }
 {!./docs_src/tutorial/automatic_id_none_refresh/tutorial002.py!}
index 502c8bf9585f5fccbc6608c954cecc4434bf2a2d..0152ff669e3d17bb45aa0110e7f3aed769172262 100644 (file)
@@ -149,10 +149,13 @@ Let's say that for some reason you hate the idea of having all the database mode
 
 You can also do it. 😎 There's a couple of things to keep in mind. 🤓
 
-!!! warning
-    This is a bit more advanced.
+/// warning
 
-    If the solution above already worked for you, that might be enough for you, and you can continue in the next chapter. 🤓
+This is a bit more advanced.
+
+If the solution above already worked for you, that might be enough for you, and you can continue in the next chapter. 🤓
+
+///
 
 Let's assume that now the file structure is:
 
index beda24a515a6f72ea6c406c4af08c67c21a406ea..0803432a2877c6eccf2a8bfa2faf6379662f03bf 100644 (file)
@@ -37,8 +37,11 @@ Each row in the table `hero` will point to a row in the table `team`:
 
 <img alt="table relationships" src="/img/tutorial/relationships/select/relationships2.svg">
 
-!!! info
-    We will later update **Spider-Boy** to add him to the **Preventers** team too, but not yet.
+/// info
+
+We will later update **Spider-Boy** to add him to the **Preventers** team too, but not yet.
+
+///
 
 We will continue with the code in the previous example and we will add more things to it.
 
index 5a9a420a1bfe83314e25a7f5f6af945ff4507a07..5500fcc9039c7e5d0d50dd73522312e93d29eaad 100644 (file)
@@ -126,8 +126,11 @@ This is the name of the **table** in the database, so it is `"team"`, not the na
 
 If you had a custom table name, you would use that custom table name.
 
-!!! info
-    You can learn about setting a custom table name for a model in the Advanced User Guide.
+/// info
+
+You can learn about setting a custom table name for a model in the Advanced User Guide.
+
+///
 
 ### Create the Tables
 
@@ -167,8 +170,11 @@ And as before, we'll call this function from another function `main()`, and we'l
 
 ## Run the Code
 
-!!! tip
-    Before running the code, make sure you delete the file `database.db` to make sure you start from scratch.
+/// tip
+
+Before running the code, make sure you delete the file `database.db` to make sure you start from scratch.
+
+///
 
 If we run the code we have up to now, it will go and create the database file `database.db` and the tables in it we just defined, `team` and `hero`:
 
index 76e0c7bde41f5008b78e04caa393f46b117a40d5..aa57e432fa00e5642ce2ab07bdcf99a46714de7d 100644 (file)
@@ -6,7 +6,10 @@ But the main advantage and feature of SQL databases is being able to handle rela
 
 Let's see how to use **SQLModel** to manage connected data in the next chapters. 🤝
 
-!!! tip
-    We will extend this further in the next group of chapters making it even more convenient to work with in Python code, using **relationship attributes**.
+/// tip
 
-    But you should start in this group of chapters first. 🤓
+We will extend this further in the next group of chapters making it even more convenient to work with in Python code, using **relationship attributes**.
+
+But you should start in this group of chapters first. 🤓
+
+///
index 0ef3bccf438fd0850fca1a24e9184f83541e14dc..91bafc47f2b8f2f3e0836f2fad2d88f45364040d 100644 (file)
@@ -62,8 +62,11 @@ FROM hero, team
 WHERE hero.team_id = team.id
 ```
 
-!!! info
-    Because we have two columns called `name`, one for `hero` and one for `team`, we can specify them with the prefix of the table name and the dot to make it explicit what we refer to.
+/// info
+
+Because we have two columns called `name`, one for `hero` and one for `team`, we can specify them with the prefix of the table name and the dot to make it explicit what we refer to.
+
+///
 
 Notice that now in the `WHERE` part we are not comparing one column with a literal value (like `hero.name = "Deadpond"`), but we are comparing two columns.
 
@@ -99,14 +102,17 @@ You can go ahead and try it in **DB Browser for SQLite**:
 
 <img class="shadow" src="/img/tutorial/relationships/select/image01.png">
 
-!!! note
-    Wait, what about Spider-Boy? 😱
+/// note
+
+Wait, what about Spider-Boy? 😱
 
-    He doesn't have a team, so his `team_id` is `NULL` in the database. And this SQL is comparing that `NULL` from the `team_id` with all the `id` fields in the rows in the `team` table.
+He doesn't have a team, so his `team_id` is `NULL` in the database. And this SQL is comparing that `NULL` from the `team_id` with all the `id` fields in the rows in the `team` table.
 
-    As there's no team with an ID of `NULL`, it doesn't find a match.
+As there's no team with an ID of `NULL`, it doesn't find a match.
 
-    But we'll see how to fix that later with a `LEFT JOIN`.
+But we'll see how to fix that later with a `LEFT JOIN`.
+
+///
 
 ## Select Related Data with **SQLModel**
 
@@ -164,10 +170,13 @@ For each iteration in the `for` loop we get a a tuple with an instance of the cl
 
 And in this `for` loop we assign them to the variable `hero` and the variable `team`.
 
-!!! info
-    There was a lot of research, design, and work behind **SQLModel** to make this provide the best possible developer experience.
+/// info
+
+There was a lot of research, design, and work behind **SQLModel** to make this provide the best possible developer experience.
 
-    And you should get autocompletion and inline errors in your editor for both `hero` and `team`. 🎉
+And you should get autocompletion and inline errors in your editor for both `hero` and `team`. 🎉
+
+///
 
 ## Add It to Main
 
@@ -281,10 +290,13 @@ Also in **DB Browser for SQLite**:
 
 <img class="shadow" src="/img/tutorial/relationships/select/image02.png">
 
-!!! tip
-    Why bother with all this if the result is the same?
+/// tip
+
+Why bother with all this if the result is the same?
+
+This `JOIN` will be useful in a bit to be able to also get Spider-Boy, even if he doesn't have a team.
 
-    This `JOIN` will be useful in a bit to be able to also get Spider-Boy, even if he doesn't have a team.
+///
 
 ## Join Tables in **SQLModel**
 
@@ -420,8 +432,11 @@ And that would return the following result, including **Spider-Boy** 🎉:
 </tr>
 </table>
 
-!!! tip
-    The only difference between this query and the previous is that extra `LEFT OUTER`.
+/// tip
+
+The only difference between this query and the previous is that extra `LEFT OUTER`.
+
+///
 
 And here's another of the SQL variations, you could write `LEFT OUTER JOIN` or just `LEFT JOIN`, it means the same.
 
index 4437f15a6d39d7d5269072b8a51d9ba527283259..72be6db297508fd8b8da0a9c51a05c4a753b1526 100644 (file)
@@ -42,8 +42,11 @@ Click the button <kbd>New Database</kbd>.
 
 A dialog should show up. Go to the [project directory you created](./index.md#create-a-project){.internal-link target=_blank} and save the file with a name of `database.db`.
 
-!!! tip
-    It's common to save SQLite database files with an extension of `.db`. Sometimes also `.sqlite`.
+/// tip
+
+It's common to save SQLite database files with an extension of `.db`. Sometimes also `.sqlite`.
+
+///
 
 ## Create a Table
 
index d0d27424d181cc13a6747de65c2e73691f77b6e4..48341b96e048366d95f9b963c2a6c158f955d505 100644 (file)
@@ -33,8 +33,11 @@ The first thing we need to do is create a class to represent the data in the tab
 
 A class like this that represents some data is commonly called a **model**.
 
-!!! tip
-    That's why this package is called `SQLModel`. Because it's mainly used to create **SQL Models**.
+/// tip
+
+That's why this package is called `SQLModel`. Because it's mainly used to create **SQL Models**.
+
+///
 
 For that, we will import `SQLModel` (plus other things we will also use) and create a class `Hero` that inherits from `SQLModel` and represents the **table model** for our heroes:
 
@@ -57,10 +60,13 @@ This class `Hero` **represents the table** for our heroes. And each instance we
 
 We use the config `table=True` to tell **SQLModel** that this is a **table model**, it represents a table.
 
-!!! info
-    It's also possible to have models without `table=True`, those would be only **data models**, without a table in the database, they would not be **table models**.
+/// info
+
+It's also possible to have models without `table=True`, those would be only **data models**, without a table in the database, they would not be **table models**.
 
-    Those **data models** will be **very useful later**, but for now, we'll just keep adding the `table=True` configuration.
+Those **data models** will be **very useful later**, but for now, we'll just keep adding the `table=True` configuration.
+
+///
 
 ## Define the Fields, Columns
 
@@ -112,8 +118,11 @@ And we also set the default value of `age` to `None`.
 
 </details>
 
-!!! tip
-    We also define `id` with `Optional`. But we will talk about `id` below.
+/// tip
+
+We also define `id` with `Optional`. But we will talk about `id` below.
+
+///
 
 This way, we tell **SQLModel** that `age` is not required when validating data and that it has a default value of `None`.
 
@@ -121,10 +130,13 @@ And we also tell it that, in the SQL database, the default value of `age` is `NU
 
 So, this column is "nullable" (can be set to `NULL`).
 
-!!! info
-    In terms of **Pydantic**, `age` is an **optional field**.
+/// info
+
+In terms of **Pydantic**, `age` is an **optional field**.
 
-    In terms of **SQLAlchemy**, `age` is a **nullable column**.
+In terms of **SQLAlchemy**, `age` is a **nullable column**.
+
+///
 
 ### Primary Key `id`
 
@@ -207,10 +219,13 @@ Creating the **engine** is very simple, just call `create_engine()` with a URL f
 
 You should normally have a single **engine** object for your whole application and re-use it everywhere.
 
-!!! tip
-    There's another related thing called a **Session** that normally should *not* be a single object per application.
+/// tip
+
+There's another related thing called a **Session** that normally should *not* be a single object per application.
 
-    But we will talk about it later.
+But we will talk about it later.
+
+///
 
 ### Engine Database URL
 
@@ -272,8 +287,11 @@ engine = create_engine(sqlite_url)
 
 ### Engine Technical Details
 
-!!! tip
-    If you didn't know about SQLAlchemy before and are just learning **SQLModel**, you can probably skip this section, scroll below.
+/// tip
+
+If you didn't know about SQLAlchemy before and are just learning **SQLModel**, you can probably skip this section, scroll below.
+
+///
 
 You can read a lot more about the engine in the <a href="https://docs.sqlalchemy.org/en/14/tutorial/engine.html" class="external-link" target="_blank">SQLAlchemy documentation</a>.
 
@@ -289,12 +307,15 @@ Now everything is in place to finally create the database and table:
 {!./docs_src/tutorial/create_db_and_table/tutorial001.py!}
 ```
 
-!!! tip
-    Creating the engine doesn't create the `database.db` file.
+/// tip
+
+Creating the engine doesn't create the `database.db` file.
+
+But once we run `SQLModel.metadata.create_all(engine)`, it creates the `database.db` file **and** creates the `hero` table in that database.
 
-    But once we run `SQLModel.metadata.create_all(engine)`, it creates the `database.db` file **and** creates the `hero` table in that database.
+Both things are done in this single step.
 
-    Both things are done in this single step.
+///
 
 Let's unwrap that:
 
@@ -404,8 +425,11 @@ Put the code it in a file `app.py` if you haven't already.
 
 </details>
 
-!!! tip
-    Remember to [activate the virtual environment](./index.md#create-a-python-virtual-environment){.internal-link target=_blank} before running it.
+/// tip
+
+Remember to [activate the virtual environment](./index.md#create-a-python-virtual-environment){.internal-link target=_blank} before running it.
+
+///
 
 Now run the program with Python:
 
@@ -442,20 +466,23 @@ INFO Engine COMMIT
 
 </div>
 
-!!! info
-    I simplified the output above a bit to make it easier to read.
+/// info
 
-    But in reality, instead of showing:
+I simplified the output above a bit to make it easier to read.
 
-    ```
-    INFO Engine BEGIN (implicit)
-    ```
+But in reality, instead of showing:
 
-    it would show something like:
+```
+INFO Engine BEGIN (implicit)
+```
+
+it would show something like:
+
+```
+2021-07-25 21:37:39,175 INFO sqlalchemy.engine.Engine BEGIN (implicit)
+```
 
-    ```
-    2021-07-25 21:37:39,175 INFO sqlalchemy.engine.Engine BEGIN (implicit)
-    ```
+///
 
 ### `TEXT` or `VARCHAR`
 
@@ -479,8 +506,11 @@ Additional to the difference between those two data types, some databases like M
 
 To make it easier to start using **SQLModel** right away independent of the database you use (even with MySQL), and without any extra configurations, by default, `str` fields are interpreted as `VARCHAR` in most databases and `VARCHAR(255)` in MySQL, this way you know the same class will be compatible with the most popular databases without extra effort.
 
-!!! tip
-    You will learn how to change the maximum length of string columns later in the Advanced Tutorial - User Guide.
+/// tip
+
+You will learn how to change the maximum length of string columns later in the Advanced Tutorial - User Guide.
+
+///
 
 ### Verify the Database
 
@@ -519,8 +549,11 @@ We don't want that to happen like that, only when we **intend** it to happen, th
 
 Now we would be able to, for example, import the `Hero` class in some other file without having those **side effects**.
 
-!!! tip
-    😅 **Spoiler alert**: The function is called `create_db_and_tables()` because we will have more **tables** in the future with other classes apart from `Hero`. 🚀
+/// tip
+
+😅 **Spoiler alert**: The function is called `create_db_and_tables()` because we will have more **tables** in the future with other classes apart from `Hero`. 🚀
+
+///
 
 ### Create Data as a Script
 
@@ -528,10 +561,13 @@ We prevented the side effects when importing something from your `app.py` file.
 
 But we still want it to **create the database and table** when we call it with Python directly as an independent script from the terminal, just as as above.
 
-!!! tip
-    Think of the word **script** and **program** as interchangeable.
+/// tip
 
-    The word **script** often implies that the code could be run independently and easily. Or in some cases it refers to a relatively simple program.
+Think of the word **script** and **program** as interchangeable.
+
+The word **script** often implies that the code could be run independently and easily. Or in some cases it refers to a relatively simple program.
+
+///
 
 For that we can use the special variable `__name__` in an `if` block:
 
@@ -559,10 +595,13 @@ $ python app.py
 from app import Hero
 ```
 
-!!! tip
-    That `if` block using `if __name__ == "__main__":` is sometimes called the "**main block**".
+/// tip
+
+That `if` block using `if __name__ == "__main__":` is sometimes called the "**main block**".
+
+The official name (in the <a href="https://docs.python.org/3/library/__main__.html" class="external-link" target="_blank">Python docs</a>) is "**Top-level script environment**".
 
-    The official name (in the <a href="https://docs.python.org/3/library/__main__.html" class="external-link" target="_blank">Python docs</a>) is "**Top-level script environment**".
+///
 
 #### More details
 
@@ -614,8 +653,11 @@ if __name__ == "__main__":
 
 ...will **not** be executed.
 
-!!! info
-    For more information, check <a href="https://docs.python.org/3/library/__main__.html" class="external-link" target="_blank">the official Python docs</a>.
+/// info
+
+For more information, check <a href="https://docs.python.org/3/library/__main__.html" class="external-link" target="_blank">the official Python docs</a>.
+
+///
 
 ## Last Review
 
@@ -631,8 +673,11 @@ Now, let's give the code a final look:
 
 {!./docs_src/tutorial/create_db_and_table/annotations/en/tutorial003.md!}
 
-!!! tip
-    Review what each line does by clicking each number bubble in the code. 👆
+/// tip
+
+Review what each line does by clicking each number bubble in the code. 👆
+
+///
 
 ## Recap
 
index 590b2ece525b671e4131455847305328c8313ee8..34dd3be423e3ce545e26f07490483d8c07033367 100644 (file)
@@ -333,8 +333,11 @@ Now let's review all that code:
 
 {!./docs_src/tutorial/delete/annotations/en/tutorial002.md!}
 
-!!! tip
-    Check out the number bubbles to see what is done by each line of code.
+/// tip
+
+Check out the number bubbles to see what is done by each line of code.
+
+///
 
 ## Recap
 
index 8802f4ec99cbc0d58895896378ef311a6632387b..1152101eba1da1efe27b89f9d31ea2ef2a9c7cee 100644 (file)
@@ -8,8 +8,11 @@ So, we probably want to limit it.
 
 Let's use the same **offset** and **limit** we learned about in the previous tutorial chapters for the API.
 
-!!! info
-    In many cases, this is also called **pagination**.
+/// info
+
+In many cases, this is also called **pagination**.
+
+///
 
 ## Add a Limit and Offset to the Query Parameters
 
@@ -46,12 +49,15 @@ So, to prevent it, we add additional validation to the `limit` query parameter,
 
 This way, a client can decide to take fewer heroes if they want, but not more.
 
-!!! info
-    If you need to refresh how query parameters and their validation work, check out the docs in FastAPI:
+/// info
+
+If you need to refresh how query parameters and their validation work, check out the docs in FastAPI:
+
+* <a href="https://fastapi.tiangolo.com/tutorial/query-params/" class="external-link" target="_blank">Query Parameters</a>
+* <a href="https://fastapi.tiangolo.com/tutorial/query-params-str-validations/" class="external-link" target="_blank">Query Parameters and String Validations</a>
+* <a href="https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/" class="external-link" target="_blank">Path Parameters and Numeric Validations</a>
 
-    * <a href="https://fastapi.tiangolo.com/tutorial/query-params/" class="external-link" target="_blank">Query Parameters</a>
-    * <a href="https://fastapi.tiangolo.com/tutorial/query-params-str-validations/" class="external-link" target="_blank">Query Parameters and String Validations</a>
-    * <a href="https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/" class="external-link" target="_blank">Path Parameters and Numeric Validations</a>
+///
 
 ## Check the Docs UI
 
index 6845b9862d87a7c748ca069c0166ac775f3dcf7a..3833f816a90bab7d5081175b7fe455a4107b85c0 100644 (file)
@@ -136,8 +136,11 @@ But `HeroCreate` and `HeroRead` don't have `table = True`. They are only **data
 
 This also means that `SQLModel.metadata.create_all()` won't create tables in the database for `HeroCreate` and `HeroRead`, because they don't have `table = True`, which is exactly what we want. 🚀
 
-!!! tip
-    We will improve this code to avoid duplicating the fields, but for now we can continue learning with these models.
+/// tip
+
+We will improve this code to avoid duplicating the fields, but for now we can continue learning with these models.
+
+///
 
 ## Use Multiple Models to Create a Hero
 
@@ -208,10 +211,13 @@ And now that we return it, FastAPI will validate the data with the `response_mod
 
 This will validate that all the data that we promised is there and will remove any data we didn't declare.
 
-!!! tip
-    This filtering could be very important and could be a very good security feature, for example, to make sure you filter private data, hashed passwords, etc.
+/// tip
+
+This filtering could be very important and could be a very good security feature, for example, to make sure you filter private data, hashed passwords, etc.
+
+You can read more about it in the <a href="https://fastapi.tiangolo.com/tutorial/response-model/" class="external-link" target="_blank">FastAPI docs about Response Model</a>.
 
-    You can read more about it in the <a href="https://fastapi.tiangolo.com/tutorial/response-model/" class="external-link" target="_blank">FastAPI docs about Response Model</a>.
+///
 
 In particular, it will make sure that the `id` is there and that it is indeed an integer (and not `None`).
 
index 8eea6488b12ceffa42fa6c266c91a1843c141641..b06ebc2a8352584e6acfe14e1859c9714ad677e7 100644 (file)
@@ -8,8 +8,11 @@ Let's add a new *path operation* to read one single hero.
 
 We want to get the hero based on the `id`, so we will use a **path parameter** `hero_id`.
 
-!!! info
-    If you need to refresh how *path parameters* work, including their data validation, check the <a href="https://fastapi.tiangolo.com/tutorial/path-params/" class="external-link" target="_blank">FastAPI docs about Path Parameters</a>.
+/// info
+
+If you need to refresh how *path parameters* work, including their data validation, check the <a href="https://fastapi.tiangolo.com/tutorial/path-params/" class="external-link" target="_blank">FastAPI docs about Path Parameters</a>.
+
+///
 
 ```Python hl_lines="8"
 {!./docs_src/tutorial/fastapi/read_one/tutorial001.py[ln:1-4]!}
index c019f4580bbce641ff2fe0ab1d2ab775ac58dd66..29899beaffe916a43192532db37dcc1aa0592b0e 100644 (file)
@@ -100,10 +100,13 @@ Additionally, because the schemas are defined in using a standard, there are man
 
 For example, client generators, that can automatically create the code necessary to talk to your API in many languages.
 
-!!! info
-    If you are curious about the standards, FastAPI generates OpenAPI, that internally uses JSON Schema.
+/// info
 
-    You can read about all that in the <a href="https://fastapi.tiangolo.com/tutorial/first-steps/#openapi" class="external-link" target="_blank">FastAPI docs - First Steps</a>.
+If you are curious about the standards, FastAPI generates OpenAPI, that internally uses JSON Schema.
+
+You can read about all that in the <a href="https://fastapi.tiangolo.com/tutorial/first-steps/#openapi" class="external-link" target="_blank">FastAPI docs - First Steps</a>.
+
+///
 
 ## Recap
 
index 195c2e1729561da50f6f79dfe5f6ff67dd6cb915..d4265af9f857be26128551ff035cbeba60643548 100644 (file)
@@ -81,14 +81,17 @@ We import `Depends()` from `fastapi`. Then we use it in the *path operation func
 
 </details>
 
-!!! tip
-    Here's a tip about that `*,` thing in the parameters.
+/// tip
 
-    Here we are passing the parameter `session` that has a "default value" of `Depends(get_session)` before the parameter `hero`, that doesn't have any default value.
+Here's a tip about that `*,` thing in the parameters.
 
-    Python would normally complain about that, but we can use the initial "parameter" `*,` to mark all the rest of the parameters as "keyword only", which solves the problem.
+Here we are passing the parameter `session` that has a "default value" of `Depends(get_session)` before the parameter `hero`, that doesn't have any default value.
 
-    You can read more about it in the FastAPI documentation <a href="https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/#order-the-parameters-as-you-need-tricks" class="external-link" target="_blank">Path Parameters and Numeric Validations - Order the parameters as you need, tricks</a>
+Python would normally complain about that, but we can use the initial "parameter" `*,` to mark all the rest of the parameters as "keyword only", which solves the problem.
+
+You can read more about it in the FastAPI documentation <a href="https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/#order-the-parameters-as-you-need-tricks" class="external-link" target="_blank">Path Parameters and Numeric Validations - Order the parameters as you need, tricks</a>
+
+///
 
 The value of a dependency will **only be used for one request**, FastAPI will call it right before calling your code and will give you the value from that dependency.
 
index 53a5fa7d3885e8093536ca2a4114c769ff34125f..8debe579afd20c0291f1464cdfaf67fbff936727 100644 (file)
@@ -62,10 +62,13 @@ But here we will make sure we don't share the same **session** in more than one
 
 And we also need to disable it because in **FastAPI** each request could be handled by multiple interacting threads.
 
-!!! info
-    That's enough information for now, you can read more about it in the <a href="https://fastapi.tiangolo.com/async/" class="external-link" target="_blank">FastAPI docs for `async` and `await`</a>.
+/// info
 
-    The main point is, by ensuring you **don't share** the same **session** with more than one request, the code is already safe.
+That's enough information for now, you can read more about it in the <a href="https://fastapi.tiangolo.com/async/" class="external-link" target="_blank">FastAPI docs for `async` and `await`</a>.
+
+The main point is, by ensuring you **don't share** the same **session** with more than one request, the code is already safe.
+
+///
 
 ## **FastAPI** App
 
@@ -119,8 +122,11 @@ This should be called only once at startup, not before every request, so we put
 
 ## Create Heroes *Path Operation*
 
-!!! info
-    If you need a refresher on what a **Path Operation** is (an endpoint with a specific HTTP Operation) and how to work with it in FastAPI, check out the <a href="https://fastapi.tiangolo.com/tutorial/first-steps/" class="external-link" target="_blank">FastAPI First Steps docs</a>.
+/// info
+
+If you need a refresher on what a **Path Operation** is (an endpoint with a specific HTTP Operation) and how to work with it in FastAPI, check out the <a href="https://fastapi.tiangolo.com/tutorial/first-steps/" class="external-link" target="_blank">FastAPI First Steps docs</a>.
+
+///
 
 Let's create the **path operation** code to create a new hero.
 
@@ -143,12 +149,15 @@ It will be called when a user sends a request with a `POST` **operation** to the
 
 </details>
 
-!!! info
-    If you need a refresher on some of those concepts, checkout the FastAPI documentation:
+/// info
+
+If you need a refresher on some of those concepts, checkout the FastAPI documentation:
 
-    * <a href="https://fastapi.tiangolo.com/tutorial/first-steps/" class="external-link" target="_blank">First Steps</a>
-    * <a href="https://fastapi.tiangolo.com/tutorial/path-params/" class="external-link" target="_blank">Path Parameters - Data Validation and Data Conversion</a>
-    * <a href="https://fastapi.tiangolo.com/tutorial/body/" class="external-link" target="_blank">Request Body</a>
+* <a href="https://fastapi.tiangolo.com/tutorial/first-steps/" class="external-link" target="_blank">First Steps</a>
+* <a href="https://fastapi.tiangolo.com/tutorial/path-params/" class="external-link" target="_blank">Path Parameters - Data Validation and Data Conversion</a>
+* <a href="https://fastapi.tiangolo.com/tutorial/body/" class="external-link" target="_blank">Request Body</a>
+
+///
 
 ## The **SQLModel** Advantage
 
@@ -162,8 +171,11 @@ And then, because this same **SQLModel** object is not only a **Pydantic** model
 
 So we can use intuitive standard Python **type annotations**, and we don't have to duplicate a lot of the code for the database models and the API data models. 🎉
 
-!!! tip
-    We will improve this further later, but for now, it already shows the power of having **SQLModel** classes be both **SQLAlchemy** models and **Pydantic** models at the same time.
+/// tip
+
+We will improve this further later, but for now, it already shows the power of having **SQLModel** classes be both **SQLAlchemy** models and **Pydantic** models at the same time.
+
+///
 
 ## Read Heroes *Path Operation*
 
@@ -226,11 +238,14 @@ $ uvicorn main:app
 
 </div>
 
-!!! info
-    The command `uvicorn main:app` refers to:
+/// info
+
+The command `uvicorn main:app` refers to:
+
+* `main`: the file `main.py` (the Python "module").
+* `app`: the object created inside of `main.py` with the line `app = FastAPI()`.
 
-    * `main`: the file `main.py` (the Python "module").
-    * `app`: the object created inside of `main.py` with the line `app = FastAPI()`.
+///
 
 ### Uvicorn `--reload`
 
index cc6ad65c6f4d4715a789a7d9500e8642d76885c3..ea084335b422324dcee2bc73feb037f2cc3843e5 100644 (file)
@@ -71,8 +71,11 @@ Let's start with a simple test, with just the basic test code we need the check
 
 {!./docs_src/tutorial/fastapi/app_testing/tutorial001/annotations/en/test_main_001.md!}
 
-!!! tip
-    Check out the number bubbles to see what is done by each line of code.
+/// tip
+
+Check out the number bubbles to see what is done by each line of code.
+
+///
 
 That's the **core** of the code we need for all the tests later.
 
@@ -116,8 +119,11 @@ That way we protect the production database and we have better control of the da
 
 {!./docs_src/tutorial/fastapi/app_testing/tutorial001/annotations/en/test_main_002.md!}
 
-!!! tip
-    Check out the number bubbles to see what is done by each line of code.
+/// tip
+
+Check out the number bubbles to see what is done by each line of code.
+
+///
 
 ## Create the Engine and Session for Testing
 
@@ -197,8 +203,11 @@ We just have to change a couple of parameters in the **engine**.
 
 {!./docs_src/tutorial/fastapi/app_testing/tutorial001/annotations/en/test_main_004.md!}
 
-!!! tip
-    Check out the number bubbles to see what is done by each line of code.
+/// tip
+
+Check out the number bubbles to see what is done by each line of code.
+
+///
 
 That's it, now the test will run using the **in-memory database**, which will be faster and probably safer.
 
@@ -214,8 +223,11 @@ Do we really have to duplicate all that for **each test**? No, we can do better!
 
 We are using **pytest** to run the tests. And pytest also has a very similar concept to the **dependencies in FastAPI**.
 
-!!! info
-    In fact, pytest was one of the things that inspired the design of the dependencies in FastAPI.
+/// info
+
+In fact, pytest was one of the things that inspired the design of the dependencies in FastAPI.
+
+///
 
 It's a way for us to declare some **code that should be run before** each test and **provide a value** for the test function (that's pretty much the same as FastAPI dependencies).
 
@@ -237,8 +249,11 @@ Let's see the first code example with a fixture:
 
 {!./docs_src/tutorial/fastapi/app_testing/tutorial001/annotations/en/test_main_005.md!}
 
-!!! tip
-    Check out the number bubbles to see what is done by each line of code.
+/// tip
+
+Check out the number bubbles to see what is done by each line of code.
+
+///
 
 **pytest** fixtures work in a very similar way to FastAPI dependencies, but have some minor differences:
 
@@ -274,8 +289,11 @@ So, we can create a **client fixture** that will be used in all the tests, and i
 
 {!./docs_src/tutorial/fastapi/app_testing/tutorial001/annotations/en/test_main_006.md!}
 
-!!! tip
-    Check out the number bubbles to see what is done by each line of code.
+/// tip
+
+Check out the number bubbles to see what is done by each line of code.
+
+///
 
 Now we have a **client fixture** that, in turn, uses the **session fixture**.
 
@@ -306,10 +324,13 @@ Let's add some more tests:
 
 </details>
 
-!!! tip
-    It's always **good idea** to not only test the normal case, but also that **invalid data**, **errors**, and **corner cases** are handled correctly.
+/// tip
+
+It's always **good idea** to not only test the normal case, but also that **invalid data**, **errors**, and **corner cases** are handled correctly.
+
+That's why we add these two extra tests here.
 
-    That's why we add these two extra tests here.
+///
 
 Now, any additional test functions can be as **simple** as the first one, they just have to **declare the `client` parameter** to get the `TestClient` **fixture** with all the database stuff setup. Nice! 😎
 
index 0b5292bd297a8b7e2f54ac064311c19217762ad6..df04600510883c66489a62e849d28a94571d03ce 100644 (file)
@@ -12,10 +12,13 @@ So, we need to have all those fields **marked as optional**.
 
 And because the `HeroBase` has some of them as *required* and not optional, we will need to **create a new model**.
 
-!!! tip
-    Here is one of those cases where it probably makes sense to use an **independent model** instead of trying to come up with a complex tree of models inheriting from each other.
+/// tip
 
-    Because each field is **actually different** (we just change it to `Optional`, but that's already making it different), it makes sense to have them in their own model.
+Here is one of those cases where it probably makes sense to use an **independent model** instead of trying to come up with a complex tree of models inheriting from each other.
+
+Because each field is **actually different** (we just change it to `Optional`, but that's already making it different), it makes sense to have them in their own model.
+
+///
 
 So, let's create this new `HeroUpdate` model:
 
index 773ab3b4a967e86f00cf2fd4aa91f77606cd2836..1e78c9c4f756a87941038b69b2f699e4639cf079 100644 (file)
@@ -57,8 +57,11 @@ $ cd sqlmodel-tutorial
 
 </div>
 
-!!! tip
-    Make sure you don't name it also `sqlmodel`, so that you don't end up overriding the name of the package.
+/// tip
+
+Make sure you don't name it also `sqlmodel`, so that you don't end up overriding the name of the package.
+
+///
 
 ### Make sure you have Python
 
@@ -119,61 +122,68 @@ In very short, a virtual environment is a small directory that contains a copy o
 
 And when you "activate" it, any package that you install, for example with `pip`, will be installed in that virtual environment.
 
-!!! tip
-    There are other tools to manage virtual environments, like <a href="https://python-poetry.org/" class="external-link" target="_blank">Poetry</a>.
+/// tip
+
+There are other tools to manage virtual environments, like <a href="https://python-poetry.org/" class="external-link" target="_blank">Poetry</a>.
 
-    And there are alternatives that are particularly useful for deployment like <a href="https://docs.docker.com/get-started/" class="external-link" target="_blank">Docker</a> and other types of containers. In this case, the "virtual environment" is not just the Python standard files and the installed packages, but the whole system.
+And there are alternatives that are particularly useful for deployment like <a href="https://docs.docker.com/get-started/" class="external-link" target="_blank">Docker</a> and other types of containers. In this case, the "virtual environment" is not just the Python standard files and the installed packages, but the whole system.
+
+///
 
 Go ahead and create a Python virtual environment for this project. And make sure to also upgrade `pip`.
 
 Here are the commands you could use:
 
-=== "Linux, macOS, Linux in Windows"
-
-    <div class="termy">
-
-    ```console
-    // Remember that you might need to use python3.9 or similar 💡
-    // Create the virtual environment using the module "venv"
-    $ python3 -m venv env
-    // ...here it creates the virtual environment in the directory "env"
-    // Activate the virtual environment
-    $ source ./env/bin/activate
-    // Verify that the virtual environment is active
-    # (env) $$ which python
-    // The important part is that it is inside the project directory, at "code/sqlmodel-tutorial/env/bin/python"
-    /home/leela/code/sqlmodel-tutorial/env/bin/python
-    // Use the module "pip" to install and upgrade the package "pip" 🤯
-    # (env) $$ python -m pip install --upgrade pip
-    ---> 100%
-    Successfully installed pip
-    ```
-
-    </div>
-
-=== "Windows PowerShell"
-
-    <div class="termy">
-
-    ```console
-    // Create the virtual environment using the module "venv"
-    # >$ python3 -m venv env
-    // ...here it creates the virtual environment in the directory "env"
-    // Activate the virtual environment
-    # >$ .\env\Scripts\Activate.ps1
-    // Verify that the virtual environment is active
-    # (env) >$ Get-Command python
-    // The important part is that it is inside the project directory, at "code\sqlmodel-tutorial\env\python.exe"
-    CommandType    Name    Version     Source
-    -----------    ----    -------     ------
-    Application    python  0.0.0.0     C:\Users\leela\code\sqlmodel-tutorial\env\python.exe
-    // Use the module "pip" to install and upgrade the package "pip" 🤯
-    # (env) >$ python3 -m pip install --upgrade pip
-    ---> 100%
-    Successfully installed pip
-    ```
-
-    </div>
+/// tab | Linux, macOS, Linux in Windows
+
+<div class="termy">
+
+```console
+// Remember that you might need to use python3.9 or similar 💡
+// Create the virtual environment using the module "venv"
+$ python3 -m venv env
+// ...here it creates the virtual environment in the directory "env"
+// Activate the virtual environment
+$ source ./env/bin/activate
+// Verify that the virtual environment is active
+# (env) $$ which python
+// The important part is that it is inside the project directory, at "code/sqlmodel-tutorial/env/bin/python"
+/home/leela/code/sqlmodel-tutorial/env/bin/python
+// Use the module "pip" to install and upgrade the package "pip" 🤯
+# (env) $$ python -m pip install --upgrade pip
+---> 100%
+Successfully installed pip
+```
+
+</div>
+
+///
+
+/// tab | Windows PowerShell
+
+<div class="termy">
+
+```console
+// Create the virtual environment using the module "venv"
+# >$ python3 -m venv env
+// ...here it creates the virtual environment in the directory "env"
+// Activate the virtual environment
+# >$ .\env\Scripts\Activate.ps1
+// Verify that the virtual environment is active
+# (env) >$ Get-Command python
+// The important part is that it is inside the project directory, at "code\sqlmodel-tutorial\env\python.exe"
+CommandType    Name    Version     Source
+-----------    ----    -------     ------
+Application    python  0.0.0.0     C:\Users\leela\code\sqlmodel-tutorial\env\python.exe
+// Use the module "pip" to install and upgrade the package "pip" 🤯
+# (env) >$ python3 -m pip install --upgrade pip
+---> 100%
+Successfully installed pip
+```
+
+</div>
+
+///
 
 ## Install **SQLModel**
 
index fef0081dc8320560355a32600cfd8b62d8ec3a99..9a4be7dbfb17f8fde5e41cae621ee7c06691bd9f 100644 (file)
@@ -73,12 +73,15 @@ You repeat this process **a few more times**, and you finally arrive at the lett
 
 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. 😅
+/// note  | Technical Details
 
-    That <abbr title="a recipe, a sequence of predefined steps that achieve a result">algorithm</abbr> I showed you above is called **Binary Search**.
+Do you like **fancy words**? Cool! Programmers tend to like fancy words. 😅
 
-    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.
+That <abbr title="a recipe, a sequence of predefined steps that achieve a result">algorithm</abbr> 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
 
@@ -297,10 +300,13 @@ We use the same `Field()` again as we did before, and set `index=True`. That's i
 
 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.
+/// info
+
+SQLModel (actually SQLAlchemy) will **automatically generate the index name** for you.
+
+In this case the generated name would be `ix_hero_name`.
 
-    In this case the generated name would be `ix_hero_name`.
+///
 
 ## Query Data
 
index ecf87adbad63a6db48fb52131b8b4d93b7e6477d..31b387dced62752544e6a50c4222ecfcaf1f8752 100644 (file)
@@ -70,8 +70,11 @@ You can try that SQL statement in **DB Explorer for SQLite**.
 
 Make sure to open the same database we already created by clicking <kbd>Open Database</kbd> and selecting the same `database.db` file.
 
-!!! tip
-    If you don't have that `database.db` file with the table `hero`, you can re-create it by running the Python program at the top. 👆
+/// tip
+
+If you don't have that `database.db` file with the table `hero`, you can re-create it by running the Python program at the top. 👆
+
+///
 
 Then go to the <kbd>Execute SQL</kbd> tab and copy the SQL from above.
 
@@ -141,10 +144,13 @@ We'll create 3 right away, for the 3 heroes:
 
 </details>
 
-!!! tip
-    The code above in this file (the omitted code) is just the same code that you see at the top of this chapter.
+/// tip
+
+The code above in this file (the omitted code) is just the same code that you see at the top of this chapter.
 
-    The same code we used before to create the `Hero` model.
+The same code we used before to create the `Hero` model.
+
+///
 
 We are putting that in a function `create_heroes()`, to call it later once we finish it.
 
@@ -204,8 +210,11 @@ Then we can create a new session:
 
 The new `Session` takes an `engine` as a parameter. And it will use the **engine** underneath.
 
-!!! tip
-    We will see a better way to create a **session** using a `with` block later.
+/// tip
+
+We will see a better way to create a **session** using a `with` block later.
+
+///
 
 ## Add Model Instances to the Session
 
@@ -237,10 +246,13 @@ And once we are ready, we can **commit** those changes, and then the **session**
 
 This makes the interactions with the database more efficient (plus some extra benefits).
 
-!!! info "Technical Details"
-    The session will create a new transaction and execute all the SQL code in that transaction.
+/// info  | Technical Details
+
+The session will create a new transaction and execute all the SQL code in that transaction.
+
+This ensures that the data is saved in a single batch, and that it will all succeed or all fail, but it won't leave the database in a broken state.
 
-    This ensures that the data is saved in a single batch, and that it will all succeed or all fail, but it won't leave the database in a broken state.
+///
 
 ## Commit the Session Changes
 
@@ -433,8 +445,11 @@ Let's focus on the new code:
 
 {!./docs_src/tutorial/insert/annotations/en/tutorial003.md!}
 
-!!! tip
-    Review what each line does by clicking each number bubble in the code. 👆
+/// tip
+
+Review what each line does by clicking each number bubble in the code. 👆
+
+///
 
 You can now put it in a `app.py` file and run it with Python. And you will see an output like the one shown above.
 
index c8b0ddf72fe2c7087844b775f86696ff0366bf93..bcd17b2f8f92dc554c284eb59467394da4f6323f 100644 (file)
@@ -110,8 +110,11 @@ INFO Engine [no key 0.00014s] (3, 0)
 
 Great! We got only 3 heroes as we wanted.
 
-!!! tip
-    We will check out that SQL code more in a bit.
+/// tip
+
+We will check out that SQL code more in a bit.
+
+///
 
 ## Select with Offset and Limit
 
@@ -119,10 +122,13 @@ Now we can limit the results to get only the first 3.
 
 But imagine we are in a user interface showing the results in batches of 3 heroes at a time.
 
-!!! tip
-    This is commonly called "pagination". Because the user interface would normally show a "page" of a predefined number of heroes at a time.
+/// tip
+
+This is commonly called "pagination". Because the user interface would normally show a "page" of a predefined number of heroes at a time.
+
+And then you can interact with the user interface to get the next page, and so on.
 
-    And then you can interact with the user interface to get the next page, and so on.
+///
 
 How do we get the next 3?
 
index e2e34777c098a6d89364b7e3dc582799ee7782dd..2aa2cf52f97eac6fc21726b87883e3977c2ceb4a 100644 (file)
@@ -30,8 +30,11 @@ The `team` table looks like this:
 </tr>
 </table>
 
-!!! tip
-    Notice that it doesn't have any foreign key to other tables.
+/// tip
+
+Notice that it doesn't have any foreign key to other tables.
+
+///
 
 And the `hero` table looks like this:
 
@@ -106,19 +109,22 @@ Specifically, the new link table `heroteamlink` would be:
 </tr>
 </table>
 
-!!! info
-    Other names used for this **link table** are:
+/// info
+
+Other names used for this **link table** are:
+
+* association table
+* secondary table
+* junction table
+* intermediate table
+* join table
+* through table
+* relationship table
+* connection table
 
-    * association table
-    * secondary table
-    * junction table
-    * intermediate table
-    * join table
-    * through table
-    * relationship table
-    * connection table
+I'm using the term "link table" because it's short, doesn't collide with other terms already used (e.g. "relationship"), it's easy to remember how to write it, etc.
 
-    I'm using the term "link table" because it's short, doesn't collide with other terms already used (e.g. "relationship"), it's easy to remember how to write it, etc.
+///
 
 ## Link Primary Key
 
index c998175a7253c64ecdc84579ed3e0e28eef05c99..90839c61c620dd3d0e1968ba72fd83555d8aac3f 100644 (file)
@@ -18,8 +18,11 @@ A row in the table `heroteamlink` points to **one** particular hero, but a singl
 
 And also, the same row in the table `heroteamlink` points to **one** team, but a single team can be connected to **many** hero-team links, so it's also **one-to-many**.
 
-!!! tip
-    The previous many-to-many relationship was also just two one-to-many relationships combined, but now it's going to be much more explicit.
+/// tip
+
+The previous many-to-many relationship was also just two one-to-many relationships combined, but now it's going to be much more explicit.
+
+///
 
 ## Update Link Model
 
@@ -51,10 +54,13 @@ The new **relationship attributes** have their own `back_populates` pointing to
 * `team`: has `back_populates="hero_links"`, because in the `Team` model, the attribute will contain the links to the **team's heroes**.
 * `hero`: has `back_populates="team_links"`, because in the `Hero` model, the attribute will contain the links to the **hero's teams**.
 
-!!! info
-    In SQLAlchemy this is called an Association Object or Association Model.
+/// info
+
+In SQLAlchemy this is called an Association Object or Association Model.
+
+I'm calling it **Link Model** just because that's easier to write avoiding typos. But you are also free to call it however you want. 😉
 
-    I'm calling it **Link Model** just because that's easier to write avoiding typos. But you are also free to call it however you want. 😉
+///
 
 ## Update Team Model
 
index 91f2f2c0e2f9fdccb85a6836f9c53e155cb7fe52..f051c3c19048eac377dec7f42f28b4ddca487745 100644 (file)
@@ -82,10 +82,13 @@ We can use the same **relationship attributes** to include `hero_spider_boy` in
 
 </details>
 
-!!! tip
-    Because we are accessing an attribute in the models right after we commit, with `hero_spider_boy.teams` and `team_z_force.heroes`, the data is refreshed automatically.
+/// tip
 
-    So we don't have to call `session.refresh()`.
+Because we are accessing an attribute in the models right after we commit, with `hero_spider_boy.teams` and `team_z_force.heroes`, the data is refreshed automatically.
+
+So we don't have to call `session.refresh()`.
+
+///
 
 We then commit the change, refresh, and print the updated **Spider-Boy**'s heroes to confirm.
 
index eadfc62a372130aea347b03ce059f1064d235815..6419d9cd713f59bc8c4144635495eaa6c0b2d723 100644 (file)
@@ -71,8 +71,11 @@ This will return the first object in the `results` (if there was any).
 
 That way, we don't have to deal with an iterable or a list.
 
-!!! tip
-    Notice that `.first()` is a method of the `results` object, not of the `select()` statement.
+/// tip
+
+Notice that `.first()` is a method of the `results` object, not of the `select()` statement.
+
+///
 
 Although this query would find two rows, by using `.first()` we get only the first row.
 
index dbd3e8c1a30a05bce8c2356aecebfab1f19afc02..e56bcc246004f1206eda69ad901e7d78f02cfa86 100644 (file)
@@ -58,8 +58,11 @@ As you already know how this works, I won't separate that in a select `statement
 
 </details>
 
-!!! tip
-    When writing your own code, this is probably the style you will use most often, as it's shorter, more convenient, and you still get all the power of autocompletion and inline errors.
+/// tip
+
+When writing your own code, this is probably the style you will use most often, as it's shorter, more convenient, and you still get all the power of autocompletion and inline errors.
+
+///
 
 ## Print the Data
 
@@ -127,8 +130,11 @@ The first important thing is, we *haven't committed* the hero yet, so accessing
 
 But in our code, in this exact point in time, we already said that **Spider-Boy** is no longer part of the **Preventers**. 🔥
 
-!!! tip
-    We could revert that later by not committing the **session**, but that's not what we are interested in here.
+/// tip
+
+We could revert that later by not committing the **session**, but that's not what we are interested in here.
+
+///
 
 Here, at this point in the code, in memory, the code expects **Preventers** to *not include* **Spider-Boy**.
 
@@ -247,10 +253,13 @@ And we can keep the rest of the code the same:
 
 </details>
 
-!!! tip
-    This is the same section where we updated `hero_spider_boy.team` to `None` but we *haven't committed* that change yet.
+/// tip
 
-    The same section that caused a problem before.
+This is the same section where we updated `hero_spider_boy.team` to `None` but we *haven't committed* that change yet.
+
+The same section that caused a problem before.
+
+///
 
 ## Review the Result
 
@@ -336,8 +345,11 @@ So, the string `"heroes"` refers to the attribute `heroes` in the class `Team`.
 
 </details>
 
-!!! tip
-    Each **relationship attribute** points to the other one, in the other model, using `back_populates`.
+/// tip
+
+Each **relationship attribute** points to the other one, in the other model, using `back_populates`.
+
+///
 
 Although it's simple code, it can be confusing to think about 😵, because the same line has concepts related to both models in multiple places:
 
index b6e77d9b45962d067c5dba7ce4806fb0aa095219..0c2b4f1ef79d0621ba8d90f95c00556ab0bdc260 100644 (file)
@@ -123,10 +123,13 @@ And in the `Team` class, the `heroes` attribute is annotated as a list of `Hero`
 
 **SQLModel** (actually SQLAlchemy) is smart enough to know that the relationship is established by the `team_id`, as that's the foreign key that points from the `hero` table to the `team` table, so we don't have to specify that explicitly here.
 
-!!! tip
-    There's a couple of things we'll check again in some of the next chapters, about the `List["Hero"]` and the `back_populates`.
+/// tip
 
-    But for now, let's first see how to use these relationship attributes.
+There's a couple of things we'll check again in some of the next chapters, about the `List["Hero"]` and the `back_populates`.
+
+But for now, let's first see how to use these relationship attributes.
+
+///
 
 ## Next Steps
 
index 2b8b843bbd05bc36542eacb218ac16e952b40fa3..b32fb637df1f5a78608c55913c2c3d9cd95fb8f6 100644 (file)
@@ -6,9 +6,12 @@ And then we read the data together with `select()` and using `.where()` or `.joi
 
 Now we will see how to use **Relationship Attributes**, an extra feature of **SQLModel** (and SQLAlchemy) to work with the data in the database in way much more familiar way, and closer to normal Python code.
 
-!!! info
-    When I say "**relationship**" I mean the standard dictionary term, of data related to other data.
+/// info
 
-    I'm not using the term "**relation**" that is the technical, academical, SQL term for a single table.
+When I say "**relationship**" I mean the standard dictionary term, of data related to other data.
+
+I'm not using the term "**relation**" that is the technical, academical, SQL term for a single table.
+
+///
 
 And using those **relationship attributes** is where a tool like **SQLModel** really shines. ✨
index 78e4207ae5a6c07de844428a18246265f99faef6..3a281a203818893a11628b42686a35a9f03dc92d 100644 (file)
@@ -77,10 +77,13 @@ So, the highlighted block above, has the same results as the block below:
 
 </details>
 
-!!! tip
-    The automatic data fetching will work as long as the starting object (in this case the `Hero`) is associated with an **open** session.
+/// tip
 
-    For example, here, **inside** a `with` block with a `Session` object.
+The automatic data fetching will work as long as the starting object (in this case the `Hero`) is associated with an **open** session.
+
+For example, here, **inside** a `with` block with a `Session` object.
+
+///
 
 ## Get a List of Relationship Objects
 
index da77dad6bb47bf3cb8fb8603cc0e2b6ad555e703..e51e71c106a4afd7a82a6ade8128ecdfc9934210 100644 (file)
@@ -29,5 +29,8 @@ And of course, **SQLModel** can also understand it in the string correctly. ✨
 
 That is actually part of Python, it's the current official solution to handle it.
 
-!!! info
-    There's a lot of work going on in Python itself to make that simpler and more intuitive, and find ways to make it possible to not wrap the class in a string.
+/// info
+
+There's a lot of work going on in Python itself to make that simpler and more intuitive, and find ways to make it possible to not wrap the class in a string.
+
+///
index e2f9af447c925df8519307780187b763a839b8a0..43003231c9b2aae574e13de020f6cb79c8faafd7 100644 (file)
@@ -79,12 +79,15 @@ You can try that out in **DB Browser for SQLite**:
 
 <img class="shadow" src="/img/tutorial/select/image01.png">
 
-!!! warning
-    Here we are getting all the rows.
+/// warning
 
-    If you have thousands of rows, that could be expensive to compute for the database.
+Here we are getting all the rows.
 
-    You would normally want to filter the rows to receive only the ones you want. But we'll learn about that later in the next chapter.
+If you have thousands of rows, that could be expensive to compute for the database.
+
+You would normally want to filter the rows to receive only the ones you want. But we'll learn about that later in the next chapter.
+
+///
 
 ### A SQL Shortcut
 
@@ -240,10 +243,13 @@ We pass the class model `Hero` to the `select()` function. And that tells it tha
 
 And notice that in the `select()` function we don't explicitly specify the `FROM` part. It is already obvious to **SQLModel** (actually to SQLAlchemy) that we want to select `FROM` the table `hero`, because that's the one associated with the `Hero` class model.
 
-!!! tip
-    The value of the `statement` returned by `select()` is a special object that allows us to do other things.
+/// tip
+
+The value of the `statement` returned by `select()` is a special object that allows us to do other things.
+
+I'll tell you about that in the next chapters.
 
-    I'll tell you about that in the next chapters.
+///
 
 ## Execute the Statement
 
@@ -360,8 +366,11 @@ Let's review the code up to this point:
 
 {!./docs_src/tutorial/select/annotations/en/tutorial002.md!}
 
-!!! tip
-    Check out the number bubbles to see what is done by each line of code.
+/// tip
+
+Check out the number bubbles to see what is done by each line of code.
+
+///
 
 Here it starts to become more evident why we should have a single **engine** for the whole application, but different **sessions** for each group of operations.
 
@@ -373,10 +382,13 @@ And the second section reading data from the database could be in another functi
 
 So, both sections could be in **different places** and would need their own sessions.
 
-!!! info
-    To be fair, in this example all that code could actually share the same **session**, there's actually no need to have two here.
+/// info
+
+To be fair, in this example all that code could actually share the same **session**, there's actually no need to have two here.
 
-    But it allows me to show you how they could be separated and to reinforce the idea that you should have **one engine** per application, and **multiple sessions**, one per each group of operations.
+But it allows me to show you how they could be separated and to reinforce the idea that you should have **one engine** per application, and **multiple sessions**, one per each group of operations.
+
+///
 
 ## Get a List of `Hero` Objects
 
@@ -415,8 +427,11 @@ After printing it, we would see something like:
 ]
 ```
 
-!!! info
-    It would actually look more compact, I'm formatting it a bit for you to see that it is actually a list with all the data.
+/// info
+
+It would actually look more compact, I'm formatting it a bit for you to see that it is actually a list with all the data.
+
+///
 
 ## Compact Version
 
@@ -461,8 +476,11 @@ SQLAchemy also has it's own `select`, and SQLModel's `select` uses SQLAlchemy's
 
 But SQLModel's version does a lot of **tricks** with type annotations to make sure you get the best **editor support** possible, no matter if you use **VS Code**, **PyCharm**, or something else. ✨
 
-!!! info
-    There was a lot of work and research, with different versions of the internal code, to improve this as much as possible. 🤓
+/// info
+
+There was a lot of work and research, with different versions of the internal code, to improve this as much as possible. 🤓
+
+///
 
 ### SQLModel's `session.exec`
 
@@ -492,10 +510,13 @@ On top of that, **SQLModel**'s `session.exec()` also does some tricks to reduce
 
 But SQLModel's `Session` still has access to `session.execute()` too.
 
-!!! tip
-    Your editor will give you autocompletion for both `session.exec()` and `session.execute()`.
+/// tip
+
+Your editor will give you autocompletion for both `session.exec()` and `session.execute()`.
+
+📢 Remember to **always use `session.exec()`** to get the best editor support and developer experience.
 
-    📢 Remember to **always use `session.exec()`** to get the best editor support and developer experience.
+///
 
 ### Caveats of **SQLModel** Flavor
 
index 56cc7ac1a5c8e5f79053c2e783685d5a9f2b2161..47ee881adc26caccd0251ce682e1c5d888c4749f 100644 (file)
@@ -41,12 +41,15 @@ And the second part, with the `WHERE`, defines to which rows it should apply tha
 
 In this case, as we only have one hero with the name `"Spider-Boy"`, it will only apply the update in that row.
 
-!!! info
-    Notice that in the `UPDATE` the single equals sign (`=`) means **assignment**, setting a column to some value.
+/// info
 
-    And in the `WHERE` the same single equals sign (`=`) is used for **comparison** between two values, to find rows that match.
+Notice that in the `UPDATE` the single equals sign (`=`) means **assignment**, setting a column to some value.
 
-    This is in contrast to Python and most programming languages, where a single equals sign (`=`) is used for assignment, and two equal signs (`==`) are used for comparisons.
+And in the `WHERE` the same single equals sign (`=`) is used for **comparison** between two values, to find rows that match.
+
+This is in contrast to Python and most programming languages, where a single equals sign (`=`) is used for assignment, and two equal signs (`==`) are used for comparisons.
+
+///
 
 You can try that in **DB Browser for SQLite**:
 
@@ -69,16 +72,19 @@ After that update, the data in the table will look like this, with the new age f
 </tr>
 </table>
 
-!!! tip
-    It will probably be more common to find the row to update by `id`, for example:
+/// tip
+
+It will probably be more common to find the row to update by `id`, for example:
+
+```SQL
+UPDATE hero
+SET age=16
+WHERE id = 2
+```
 
-    ```SQL
-    UPDATE hero
-    SET age=16
-    WHERE id = 2
-    ```
+But in the example above I used `name` to make it more intuitive.
 
-    But in the example above I used `name` to make it more intuitive.
+///
 
 Now let's do the same update in code, with **SQLModel**.
 
@@ -143,8 +149,11 @@ Hero: name='Spider-Boy' secret_name='Pedro Parqueador' age=None id=2
 
 </div>
 
-!!! tip
-    Notice that by this point, the hero still doesn't have an age.
+/// tip
+
+Notice that by this point, the hero still doesn't have an age.
+
+///
 
 ## Set a Field Value
 
@@ -333,8 +342,11 @@ Now let's review all that code:
 
 {!./docs_src/tutorial/update/annotations/en/tutorial002.md!}
 
-!!! tip
-    Check out the number bubbles to see what is done by each line of code.
+/// tip
+
+Check out the number bubbles to see what is done by each line of code.
+
+///
 
 ## Multiple Updates
 
@@ -361,8 +373,11 @@ This also means that you can update several fields (attributes, columns) at once
 
 </details>
 
-!!! tip
-    Review what each line does by clicking each number bubble in the code. 👆
+/// tip
+
+Review what each line does by clicking each number bubble in the code. 👆
+
+///
 
 ## Recap
 
index a3bf6b052946684abdc8cf0fecc770a04a0e8566..541b0833bc4d2904a4ea9d53b55e51b61a99a892 100644 (file)
@@ -81,10 +81,13 @@ Then the database will bring a table like this:
 </tr>
 </table>
 
-!!! tip
-    Even if the result is only one row, the database always returns a **table**.
+/// tip
 
-    In this case, a table with only one row.
+Even if the result is only one row, the database always returns a **table**.
+
+In this case, a table with only one row.
+
+///
 
 You can try that out in **DB Browser for SQLite**:
 
@@ -268,10 +271,13 @@ So, what's happening there?
 
 In the example above we are using two equal signs (`==`). That's called the "**equality operator**".
 
-!!! tip
-    An **operator** is just a symbol that is put beside one value or in the middle of two values to do something with them.
+/// tip
+
+An **operator** is just a symbol that is put beside one value or in the middle of two values to do something with them.
 
-    `==` is called the **equality** operator because it checks if two things are **equal**.
+`==` is called the **equality** operator because it checks if two things are **equal**.
+
+///
 
 When writing Python, if you write something using this equality operator (`==`) like:
 
@@ -291,8 +297,11 @@ True
 False
 ```
 
-!!! tip
-    `<`, `>`, `==`, `>=`, `<=`, and `!=` are all **operators** used for **comparisons**.
+/// tip
+
+`<`, `>`, `==`, `>=`, `<=`, and `!=` are all **operators** used for **comparisons**.
+
+///
 
 But SQLAlchemy adds some magic to the columns/fields in a **model class** to make those Python comparisons have super powers.
 
@@ -451,8 +460,11 @@ select(Hero).where(Hero.secret_name == "Pedro Parqueador")
 
 I think that alone, having better editor support, autocompletion, and inline errors, is enough to make it worth having expressions instead of keyword arguments. ✨
 
-!!! tip
-    **Expressions** also provide more features for other types of comparisons, shown down below. 👇
+/// tip
+
+**Expressions** also provide more features for other types of comparisons, shown down below. 👇
+
+///
 
 ## Exec the Statement
 
@@ -502,12 +514,15 @@ secret_name='Dive Wilson' age=None id=1 name='Deadpond'
 </div>
 
 
-!!! tip
-    The `results` object is an iterable to be used in a `for` loop.
+/// tip
+
+The `results` object is an iterable to be used in a `for` loop.
+
+Even if we got only one row, we iterate over that `results` object. Just as if it was a list of one element.
 
-    Even if we got only one row, we iterate over that `results` object. Just as if it was a list of one element.
+We'll see other ways to get the data later.
 
-    We'll see other ways to get the data later.
+///
 
 ## Other Comparisons
 
@@ -597,8 +612,11 @@ age=36 id=6 name='Dr. Weird' secret_name='Steve Weird'
 age=93 id=7 name='Captain North America' secret_name='Esteban Rogelios'
 ```
 
-!!! tip
-    Notice that it didn't select `Black Lion`, because the age is not *strictly* greater than `35`.
+/// tip
+
+Notice that it didn't select `Black Lion`, because the age is not *strictly* greater than `35`.
+
+///
 
 ### More Than or Equal
 
@@ -630,8 +648,11 @@ age=36 id=6 name='Dr. Weird' secret_name='Steve Weird'
 age=93 id=7 name='Captain North America' secret_name='Esteban Rogelios'
 ```
 
-!!! tip
-    This time we got `Black Lion` too because although the age is not *strictly* greater than `35`it is *equal* to `35`.
+/// tip
+
+This time we got `Black Lion` too because although the age is not *strictly* greater than `35`it is *equal* to `35`.
+
+///
 
 ### Less Than
 
@@ -660,8 +681,11 @@ And we get the younger one with an age in the database:
 age=32 id=4 name='Tarantula' secret_name='Natalia Roman-on'
 ```
 
-!!! tip
-    We could imagine that **Spider-Boy** is even **younger**. But because we don't know the age, it is `NULL` in the database (`None` in Python), it doesn't match any of these age comparisons with numbers.
+/// tip
+
+We could imagine that **Spider-Boy** is even **younger**. But because we don't know the age, it is `NULL` in the database (`None` in Python), it doesn't match any of these age comparisons with numbers.
+
+///
 
 ### Less Than or Equal
 
@@ -691,8 +715,11 @@ age=32 id=4 name='Tarantula' secret_name='Natalia Roman-on'
 age=35 id=5 name='Black Lion' secret_name='Trevor Challa'
 ```
 
-!!! tip
-    We get `Black Lion` here too because although the age is not *strictly* less than `35` it is *equal* to `35`.
+/// tip
+
+We get `Black Lion` here too because although the age is not *strictly* less than `35` it is *equal* to `35`.
+
+///
 
 ### Benefits of Expressions
 
@@ -925,10 +952,13 @@ col(Hero.age) > 35
 
 And with that the editor knows this code is actually fine, because this is a special **SQLModel** column.
 
-!!! tip
-    That `col()` will come handy later, giving autocompletion to several other things we can do with these special **class attributes** for columns.
+/// tip
+
+That `col()` will come handy later, giving autocompletion to several other things we can do with these special **class attributes** for columns.
+
+But we'll get there later.
 
-    But we'll get there later.
+///
 
 ## Recap
 
index 646af7c39ee8c997c569620b0d4b1f06e3f3c8fd..385738823b891dc8de0488192758fc2ba0e9ecac 100644 (file)
@@ -21,6 +21,13 @@ theme:
   - search.suggest
   - search.highlight
   - content.tabs.link
+  - navigation.indexes
+  - content.tooltips
+  - navigation.path
+  - content.code.annotate
+  - content.code.copy
+  - content.code.select
+  # - navigation.tabs
   icon:
     repo: fontawesome/brands/github-alt
   logo: img/icon-white.svg
@@ -91,20 +98,28 @@ nav:
   - release-notes.md
 
 markdown_extensions:
+- markdown.extensions.attr_list
+- markdown.extensions.tables
+- markdown.extensions.md_in_html
 - toc:
     permalink: true
-- markdown.extensions.codehilite:
-    guess_lang: false
-- admonition
-- codehilite
-- extra
 - pymdownx.superfences:
     custom_fences:
     - name: mermaid
       class: mermaid
       format: !!python/name:pymdownx.superfences.fence_code_format ''
-- pymdownx.tabbed:
-    alternate_style: true
+- pymdownx.betterem
+- pymdownx.highlight
+- pymdownx.blocks.details
+- pymdownx.blocks.admonition:
+    types:
+    - note
+    - info
+    - tip
+    - warning
+    - danger
+- pymdownx.blocks.tab:
+    alternate_style: True
 - mdx_include
 
 extra: