📝 Update link syntax to minimal Markdown

This commit is contained in:
Sebastián Ramírez
2026-03-05 19:38:00 +01:00
parent cd951c68ff
commit 73f922b2c9
32 changed files with 159 additions and 159 deletions

View File

@@ -91,7 +91,7 @@ Here we import the models, the engine, and the function to create all the tables
### Order Matters
Remember that [Order Matters](create-db-and-table.md#sqlmodel-metadata-order-matters){.internal-link target=_blank} when calling `SQLModel.metadata.create_all()`?
Remember that [Order Matters](create-db-and-table.md#sqlmodel-metadata-order-matters) when calling `SQLModel.metadata.create_all()`?
The point of that section in the docs is that you have to import the module that has the models **before** calling `SQLModel.metadata.create_all()`.

View File

@@ -413,7 +413,7 @@ And then why we didn't include `Hero` in the `.join()`. 🤔
In SQLModel (actually in SQLAlchemy), all these functions and tools try to **replicate** how it would be to work with the **SQL** language.
Remember that [`SELECT` defines the columns to get and `WHERE` how to filter them?](../where.md#select-and-where){.internal-link target=_blank}.
Remember that [`SELECT` defines the columns to get and `WHERE` how to filter them?](../where.md#select-and-where).
This also applies here, but with `JOIN` and `ON`.

View File

@@ -40,7 +40,7 @@ Click the button <kbd>New Database</kbd>.
<img class="shadow" src="/img/create-db-and-table-with-db-browser/image001.png">
A dialog should show up. Go to the [project directory you created](../virtual-environments.md#create-a-project){.internal-link target=_blank} and save the file with a name of `database.db`.
A dialog should show up. Go to the [project directory you created](../virtual-environments.md#create-a-project) and save the file with a name of `database.db`.
/// tip
@@ -159,9 +159,9 @@ And if you go back to the <kbd>Database Structure</kbd> tab, you will see that y
I will keep showing you small bits of SQL through this tutorial. And you don't have to be a SQL expert to use **SQLModel**.
But if you are curious and want to get a quick overview of SQL, I recommend the visual documentation from SQLite, on <a href="https://www.sqlite.org/lang.html" class="external-link" target="_blank">SQL As Understood By SQLite</a>.
But if you are curious and want to get a quick overview of SQL, I recommend the visual documentation from SQLite, on [SQL As Understood By SQLite](https://www.sqlite.org/lang.html).
You can start with <a href="https://www.sqlite.org/lang_createtable.html" class="external-link" target="_blank">`CREATE TABLE`</a>.
You can start with [`CREATE TABLE`](https://www.sqlite.org/lang_createtable.html).
Of course, you can also go and take a full SQL course or read a book about SQL, but you don't need more than what I'll explain here on the tutorial to start being productive with **SQLModel**. 🤓

View File

@@ -2,7 +2,7 @@
Now let's get to the code. 👩‍💻
Make sure you are inside of your project directory and with your virtual environment activated as explained in [Virtual Environments](../virtual-environments.md#create-a-project){.internal-link target=_blank}.
Make sure you are inside of your project directory and with your virtual environment activated as explained in [Virtual Environments](../virtual-environments.md#create-a-project).
We will:
@@ -176,7 +176,7 @@ SQLite supports a special database that lives all *in memory*. Hence, it's very
{* ./docs_src/tutorial/create_db_and_table/tutorial001_py310.py ln[1:16] hl[11:12,14] *}
You can read a lot more about all the databases supported by **SQLAlchemy** (and that way supported by **SQLModel**) in the <a href="https://docs.sqlalchemy.org/en/14/core/engines.html" class="external-link" target="_blank">SQLAlchemy documentation</a>.
You can read a lot more about all the databases supported by **SQLAlchemy** (and that way supported by **SQLModel**) in the [SQLAlchemy documentation](https://docs.sqlalchemy.org/en/14/core/engines.html).
### Engine Echo
@@ -202,7 +202,7 @@ If you didn't know about SQLAlchemy before and are just learning **SQLModel**, y
///
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>.
You can read a lot more about the engine in the [SQLAlchemy documentation](https://docs.sqlalchemy.org/en/14/tutorial/engine.html).
**SQLModel** defines its own `create_engine()` function. It is the same as SQLAlchemy's `create_engine()`, but with the difference that it defaults to use `future=True` (which means that it uses the style of the latest SQLAlchemy, 1.4, and the future 2.0).
@@ -327,7 +327,7 @@ Put the code in a file `app.py` if you haven't already.
/// tip
Remember to [activate the virtual environment](../virtual-environments.md#create-a-virtual-environment){.internal-link target=_blank} before running it.
Remember to [activate the virtual environment](../virtual-environments.md#create-a-virtual-environment) before running it.
///
@@ -390,7 +390,7 @@ In the example in the previous chapter we created the table using `TEXT` for som
But in this output SQLAlchemy is using `VARCHAR` instead. Let's see what's going on.
Remember that [each SQL Database has some different variations in what they support?](../databases.md#sql-the-language){.internal-link target=_blank}
Remember that [each SQL Database has some different variations in what they support?](../databases.md#sql-the-language)
This is one of the differences. Each database supports some particular **data types**, like `INTEGER` and `TEXT`.
@@ -484,7 +484,7 @@ from app import Hero
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 [Python docs](https://docs.python.org/3/library/__main__.html)) is "**Top-level script environment**".
///
@@ -540,7 +540,7 @@ if __name__ == "__main__":
/// 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>.
For more information, check [the official Python docs](https://docs.python.org/3/library/__main__.html).
///

View File

@@ -2,7 +2,7 @@
One of the use cases where **SQLModel** shines the most, and the main one why it was built, was to be combined with **FastAPI**. ✨
<a href="https://fastapi.tiangolo.com/" class="external-link" target="_blank">FastAPI</a> is a Python web framework for building web APIs created by the same <a href="https://twitter.com/tiangolo" class="external-link" target="_blank">author</a> of SQLModel. FastAPI is also built on top of **Pydantic**.
[FastAPI](https://fastapi.tiangolo.com/) is a Python web framework for building web APIs created by the same [author](https://twitter.com/tiangolo) of SQLModel. FastAPI is also built on top of **Pydantic**.
In this group of chapters we will see how to combine SQLModel **table models** representing tables in the SQL database as all the ones we have seen up to now, with **data models** that only represent data (which are actually just Pydantic models behind the scenes).
@@ -14,4 +14,4 @@ By the end we will have a **simple** but **complete** web **API** to interact wi
If you have never used FastAPI, maybe a good idea would be to go and study it a bit before continuing.
Just reading and trying the examples on the <a href="https://fastapi.tiangolo.com/" class="external-link" target="_blank">FastAPI main page</a> should be enough, and it shouldn't take you more than **10 minutes**.
Just reading and trying the examples on the [FastAPI main page](https://fastapi.tiangolo.com/) should be enough, and it shouldn't take you more than **10 minutes**.

View File

@@ -36,9 +36,9 @@ This way, a client can decide to take fewer heroes if they want, but not more.
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>
* [Query Parameters](https://fastapi.tiangolo.com/tutorial/query-params/)
* [Query Parameters and String Validations](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/)
* [Path Parameters and Numeric Validations](https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/)
///

View File

@@ -167,7 +167,7 @@ This will validate that all the data that we promised is there and will remove a
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 [FastAPI docs about Response Model](https://fastapi.tiangolo.com/tutorial/response-model/).
///

View File

@@ -10,7 +10,7 @@ We want to get the hero based on the `id`, so we will use a **path parameter** `
/// 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>.
If you need to refresh how *path parameters* work, including their data validation, check the [FastAPI docs about Path Parameters](https://fastapi.tiangolo.com/tutorial/path-params/).
///

View File

@@ -46,7 +46,7 @@ And the same way, we declared the `TeamPublic` with only the same base fields of
{* ./docs_src/tutorial/fastapi/teams/tutorial001_py310.py ln[5:7,20:21,29:34,43:44] hl[5:7,20:21,29:34,43:44] *}
Now, remember that <a href="https://fastapi.tiangolo.com/tutorial/response-model/" class="external-link" target="_blank">FastAPI uses the `response_model` to validate and **filter** the response data</a>?
Now, remember that [FastAPI uses the `response_model` to validate and **filter** the response data](https://fastapi.tiangolo.com/tutorial/response-model/)?
In this case, we used `response_model=TeamPublic` and `response_model=HeroPublic`, so FastAPI will use them to filter the response data, even if we return a **table model** that includes **relationship attributes**:

View File

@@ -8,7 +8,7 @@ Up to now, with the code we have used, the API docs know the data the clients ha
<img class="shadow" alt="Interactive API docs UI" src="/img/tutorial/fastapi/simple-hero-api/image01.png">
This interactive docs UI is powered by <a href="https://github.com/swagger-api/swagger-ui" class="external-link" target="_blank">Swagger UI</a>, and what Swagger UI does is to read a big JSON content that defines the API with all the data schemas (data shapes) using the standard <a href="https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md" class="external-link" target="_blank">OpenAPI</a>, and showing it in that nice <abbr title="User Interface">UI</abbr>.
This interactive docs UI is powered by [Swagger UI](https://github.com/swagger-api/swagger-ui), and what Swagger UI does is to read a big JSON content that defines the API with all the data schemas (data shapes) using the standard [OpenAPI](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md), and showing it in that nice <abbr title="User Interface">UI</abbr>.
FastAPI automatically **generates that OpenAPI** for Swagger UI to read it.
@@ -48,7 +48,7 @@ FastAPI will do data validation and filtering of the response with this `respons
So this works like a contract between our application and the client.
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 [FastAPI docs about `response_model`](https://fastapi.tiangolo.com/tutorial/response-model/).
## New API Docs UI
@@ -62,7 +62,7 @@ The clients will know what data they should expect.
The most visible advantage of using the `response_model` is that it shows up in the API docs UI.
But there are other advantages, like that FastAPI will do automatic <a href="https://fastapi.tiangolo.com/tutorial/response-model/" class="external-link" target="_blank">data validation and filtering</a> of the response data using this model.
But there are other advantages, like that FastAPI will do automatic [data validation and filtering](https://fastapi.tiangolo.com/tutorial/response-model/) of the response data using this model.
Additionally, because the schemas are defined in using a standard, there are many tools that can take advantage of this.
@@ -72,7 +72,7 @@ For example, client generators, that can automatically create the code necessary
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>.
You can read about all that in the [FastAPI docs - First Steps](https://fastapi.tiangolo.com/tutorial/first-steps/#openapi).
///

View File

@@ -8,7 +8,7 @@ Up to now, we have been creating a session in each *path operation*, in a `with`
{* ./docs_src/tutorial/fastapi/delete/tutorial001_py310.py ln[48:55] hl[50] *}
That's perfectly fine, but in many use cases we would want to use <a href="https://fastapi.tiangolo.com/tutorial/dependencies/" class="external-link" target="_blank">FastAPI Dependencies</a>, for example to **verify** that the client is **logged in** and get the **current user** before executing any other code in the *path operation*.
That's perfectly fine, but in many use cases we would want to use [FastAPI Dependencies](https://fastapi.tiangolo.com/tutorial/dependencies/), for example to **verify** that the client is **logged in** and get the **current user** before executing any other code in the *path operation*.
These dependencies are also very useful during **testing**, because we can **easily replace them**, and then, for example, use a new database for our tests, or put some data before the tests, etc.
@@ -38,7 +38,7 @@ Here we are passing the parameter `session` that has a "default value" of `Depen
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>
You can read more about it in the FastAPI documentation [Path Parameters and Numeric Validations - Order the parameters as you need, tricks](https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/#order-the-parameters-as-you-need-tricks)
///
@@ -86,4 +86,4 @@ You just learned how to use **FastAPI dependencies** to handle the database sess
And you will see how much these dependencies can help the more you work with FastAPI, to handle **permissions**, **authentication**, resources like database **sessions**, etc. 🚀
If you want to learn more about dependencies, checkout the <a href="https://fastapi.tiangolo.com/tutorial/dependencies/" class="external-link" target="_blank">FastAPI docs about Dependencies</a>.
If you want to learn more about dependencies, checkout the [FastAPI docs about Dependencies](https://fastapi.tiangolo.com/tutorial/dependencies/).

View File

@@ -8,7 +8,7 @@ The first step is to install FastAPI.
FastAPI is the framework to create the **web API**.
Make sure you create a [virtual environment](../../virtual-environments.md){.internal-link target=_blank}, activate it, and then install them, for example with:
Make sure you create a [virtual environment](../../virtual-environments.md), activate it, and then install them, for example with:
<div class="termy">
@@ -42,7 +42,7 @@ And we also need to disable it because in **FastAPI** each request could be hand
/// 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>.
That's enough information for now, you can read more about it in the [FastAPI docs for `async` and `await`](https://fastapi.tiangolo.com/async/).
The main point is, by ensuring you **don't share** the same **session** with more than one request, the code is already safe.
@@ -70,7 +70,7 @@ This should be called only once at startup, not before every request, so we put
/// 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>.
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 [FastAPI First Steps docs](https://fastapi.tiangolo.com/tutorial/first-steps/).
///
@@ -84,9 +84,9 @@ It will be called when a user sends a request with a `POST` **operation** to the
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>
* [First Steps](https://fastapi.tiangolo.com/tutorial/first-steps/)
* [Path Parameters - Data Validation and Data Conversion](https://fastapi.tiangolo.com/tutorial/path-params/)
* [Request Body](https://fastapi.tiangolo.com/tutorial/body/)
///
@@ -132,7 +132,7 @@ But we would **never want to *share* the same session** among different requests
In this simple example, we just create the new sessions manually in the **path operation functions**.
In future examples later we will use a <a href="https://fastapi.tiangolo.com/tutorial/dependencies/" class="external-link" target="_blank">FastAPI Dependency</a> to get the **session**, being able to share it with other dependencies and being able to replace it during testing. 🤓
In future examples later we will use a [FastAPI Dependency](https://fastapi.tiangolo.com/tutorial/dependencies/) to get the **session**, being able to share it with other dependencies and being able to replace it during testing. 🤓
## Run the **FastAPI** Server in Development Mode
@@ -154,7 +154,7 @@ $ fastapi dev main.py
/// info
The `fastapi` command uses <a href="https://www.uvicorn.org/" class="external-link" target="_blank">Uvicorn</a> underneath.
The `fastapi` command uses [Uvicorn](https://www.uvicorn.org/) underneath.
///
@@ -180,7 +180,7 @@ $ fastapi run main.py
Now you can go to that URL in your browser `http://127.0.0.1:8000`. We didn't create a *path operation* for the root path `/`, so that URL alone will only show a "Not Found" error... that "Not Found" error is produced by your FastAPI application.
But you can go to the **automatically generated interactive API documentation** at the path `/docs`: <a href="http://127.0.0.1:8000/docs" class="external-link" target="_blank">http://127.0.0.1:8000/docs</a>. ✨
But you can go to the **automatically generated interactive API documentation** at the path `/docs`: [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs). ✨
You will see that this **automatic API docs <abbr title="user interface">UI</abbr>** has the *paths* that we defined above with their *operations*, and that it already knows the shape of the data that the **path operations** will receive:

View File

@@ -18,7 +18,7 @@ Now we will see how useful it is to have this session dependency. ✨
## File Structure
Now we will have a Python project with multiple files, one file `main.py` with all the application, and one file `test_main.py` with the tests, with the same ideas from [Code Structure and Multiple Files](../code-structure.md){.internal-link target=_blank}.
Now we will have a Python project with multiple files, one file `main.py` with all the application, and one file `test_main.py` with the tests, with the same ideas from [Code Structure and Multiple Files](../code-structure.md).
The file structure is:
@@ -32,11 +32,11 @@ The file structure is:
## Testing FastAPI Applications
If you haven't done testing in FastAPI applications, first check the <a href="https://fastapi.tiangolo.com/tutorial/testing/" class="external-link" target="_blank">FastAPI docs about Testing</a>.
If you haven't done testing in FastAPI applications, first check the [FastAPI docs about Testing](https://fastapi.tiangolo.com/tutorial/testing/).
Then, we can continue here, the first step is to install the dependencies, `requests` and `pytest`.
Make sure you create a [virtual environment](../../virtual-environments.md){.internal-link target=_blank}, activate it, and then install them, for example with:
Make sure you create a [virtual environment](../../virtual-environments.md), activate it, and then install them, for example with:
<div class="termy">
@@ -144,7 +144,7 @@ Here we create all the tables in the testing database with:
SQLModel.metadata.create_all(engine)
```
But remember that [Order Matters](../create-db-and-table.md#sqlmodel-metadata-order-matters){.internal-link target=_blank} and we need to make sure all the **SQLModel** models are already defined and **imported** before calling `.create_all()`.
But remember that [Order Matters](../create-db-and-table.md#sqlmodel-metadata-order-matters) and we need to make sure all the **SQLModel** models are already defined and **imported** before calling `.create_all()`.
In this case, it all works for a little subtlety that deserves some attention.
@@ -230,7 +230,7 @@ Let's use these **fixtures** to improve our code and reduce de duplicated boiler
## Pytest Fixtures
You can read more about them in the <a href="https://docs.pytest.org/en/6.2.x/fixture.html" class="external-link" target="_blank">pytest docs for fixtures</a>, but I'll give you a short example for what we need here.
You can read more about them in the [pytest docs for fixtures](https://docs.pytest.org/en/6.2.x/fixture.html), but I'll give you a short example for what we need here.
Let's see the first code example with a fixture:

View File

@@ -26,7 +26,7 @@ So, the thief won't be able to try to use that password in another system (as ma
/// tip
You could use <a href="https://passlib.readthedocs.io/en/stable/" class="external-link" target="_blank">passlib</a> to hash passwords.
You could use [passlib](https://passlib.readthedocs.io/en/stable/) to hash passwords.
In this example we will use a fake hashing function to focus on the data changes. 🤡

View File

@@ -4,9 +4,9 @@ In this tutorial you will learn how to use **SQLModel**.
## Type hints
If you need a refresher about how to use Python type hints (type annotations), check <a href="https://fastapi.tiangolo.com/python-types/" class="external-link" target="_blank">FastAPI's Python types intro</a>.
If you need a refresher about how to use Python type hints (type annotations), check [FastAPI's Python types intro](https://fastapi.tiangolo.com/python-types/).
You can also check the <a href="https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html" class="external-link" target="_blank">mypy cheat sheet</a>.
You can also check the [mypy cheat sheet](https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html).
**SQLModel** uses type annotations for everything, this way you can use a familiar Python syntax and get all the editor support possible, with autocompletion and in-editor error checking.

View File

@@ -128,7 +128,7 @@ I'm using the term "link table" because it's short, doesn't collide with other t
## Link Primary Key
Cool, we have a link table with **just two columns**. But remember that SQL databases [require each row to have a **primary key**](../../databases.md#identifications-primary-key){.internal-link target=_blank} that **uniquely identifies** the row in that table?
Cool, we have a link table with **just two columns**. But remember that SQL databases [require each row to have a **primary key**](../../databases.md#identifications-primary-key) that **uniquely identifies** the row in that table?
Now, what is the **primary key** in this table?

View File

@@ -129,7 +129,7 @@ The `cascade_delete` parameter is set in the `Relationship()`, on the model that
Setting `cascade_delete=True` in the `Relationship()` will configure SQLAlchemy to use `cascade="all, delete-orphan"`, which is the most common and useful configuration when wanting to cascade deletes.
You can read more about it in the <a href="https://docs.sqlalchemy.org/en/20/orm/cascades.html" class="external-link" target="_blank">SQLAlchemy docs</a>.
You can read more about it in the [SQLAlchemy docs](https://docs.sqlalchemy.org/en/20/orm/cascades.html).
///
@@ -442,7 +442,7 @@ To be able to test this out with SQLite, we first need to enable foreign key sup
/// info
You can learn more about SQLite, foreign keys, and this SQL command on the <a href="https://docs.sqlalchemy.org/en/20/dialects/sqlite.html#foreign-key-support" class="external-link" target="_blank">SQLAlchemy docs</a>.
You can learn more about SQLite, foreign keys, and this SQL command on the [SQLAlchemy docs](https://docs.sqlalchemy.org/en/20/dialects/sqlite.html#foreign-key-support).
///

View File

@@ -591,7 +591,7 @@ We don't have any hero called `Ratman`, so it does not match any hero.
/// tip
You need to wrap your attribute with `col()` to use `in_`.
You can read more about it in the [Type annotations and errors](#type-annotations-and-errors){.internal-link target=_blank} section.
You can read more about it in the [Type annotations and errors](#type-annotations-and-errors) section.
///