diff --git a/docs/advanced/decimal.md b/docs/advanced/decimal.md index 02b84ab8be..868a709922 100644 --- a/docs/advanced/decimal.md +++ b/docs/advanced/decimal.md @@ -102,7 +102,7 @@ Total money: 3.300 /// 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. +Although Decimal types are supported and used on the Python side, not all databases support them. 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. 🎉 diff --git a/docs/advanced/uuid.md b/docs/advanced/uuid.md index 833ced4b7f..c244f4403d 100644 --- a/docs/advanced/uuid.md +++ b/docs/advanced/uuid.md @@ -28,7 +28,7 @@ There are several versions of UUID, some versions include the current time in th ### Distributed UUIDs -You could generate one UUID in one computer, and someone else could generate another UUID in another computer, and it would be almost **impossible** for both UUIDs to be the **same**. +You could generate one UUID on one computer, and someone else could generate another UUID on another computer, and it would be almost **impossible** for both UUIDs to be the **same**. This means that you don't have to wait for the DB to generate the ID for you, you can **generate it in code before sending it to the database**, because you can be quite certain it will be unique. diff --git a/docs/databases.md b/docs/databases.md index 3f3acb8847..18646e5d9a 100644 --- a/docs/databases.md +++ b/docs/databases.md @@ -50,7 +50,7 @@ In most of the cases, the objective of your program is to do something with data * It could be just moving **files** from one place to the other. * Or it could be taking data from the user in the **terminal** and showing it differently. -* Or a **web API** that takes some data and process it in some way, etc. +* Or a **web API** that takes some data and processes it in some way, etc. In most cases, the data *comes from outside* the program or *ends outside the program* (for example, shown on the screen, in a file, etc). @@ -108,9 +108,9 @@ Having distributed systems also creates additional challenges, so there's a high We already talked about the different ways to interact with a database and how they handle files, etc. That applies to most or all of the databases. -But there's another way to categorize databases that is very important. As you can imagine, there are many types of databases and many databases in each group. But in general, they can be separated in two big groups: "SQL Databases" and "NoSQL Databases". +But there's another way to categorize databases that is very important. As you can imagine, there are many types of databases and many databases in each group. But in general, they can be separated into two big groups: "SQL Databases" and "NoSQL Databases". -We will get to why the name "SQL" in a bit, but first, let's see what is it all about. +We will get to why the name "SQL" in a bit, but first, let's see what it is all about. ### SQLModel for SQL Databases @@ -137,7 +137,7 @@ If we worked with a single table to store our heroes, it could be like this: idnamesecret_nameageteamheadquarters -1DeadpondDive WilsonnullZ-FactorSister Margaret's Bar +1DeadpondDive WilsonnullZ-ForceSister Margaret's Bar 2Spider-BoyPedro ParqueadornullPreventersSharp Tower @@ -279,7 +279,7 @@ And because of this technical term, these **SQL Databases** are also called **Re After developing these ideas of how to store data in multiple tables they also created a **language** that could be used to interact with them. -The language is called **SQL**, the name comes from for **Structured Query Language**. +The language is called **SQL**, the name comes from **Structured Query Language**. Nevertheless, the language is not only used to *query* for data. It is also used to create records/rows, to update them, to delete them. And to manipulate the database, create tables, etc. diff --git a/docs/db-to-code.md b/docs/db-to-code.md index 976bab30a5..24d09f8e6e 100644 --- a/docs/db-to-code.md +++ b/docs/db-to-code.md @@ -119,7 +119,7 @@ The process of making sure that whatever the external user sends is safe to use It comes by default in **SQLModel** (thanks to SQLAlchemy). And many other similar tools would also provide that functionality among many other features. -Now you are ready for [a joke from xkcd](https://xkcd.com/327/): +Now you are ready for [a joke from xkcd](https://xkcd.com/327/): ![Exploits of a Mom](https://imgs.xkcd.com/comics/exploits_of_a_mom.png) @@ -244,7 +244,7 @@ These types of libraries like **SQLModel** (and of course, SQLAlchemy) that tran **ORM** means **Object-Relational Mapper**. -This is a very common term, but it also comes from quite technical and **academical** concepts 👩‍🎓: +This is a very common term, but it also comes from quite technical and **academic** concepts 👩‍🎓: * **Object**: refers to code with classes and instances, normally called "Object Oriented Programming", that's why the "**Object**" part. diff --git a/docs/features.md b/docs/features.md index 204c23697e..20f4392878 100644 --- a/docs/features.md +++ b/docs/features.md @@ -14,7 +14,7 @@ Nevertheless, SQLModel is completely **independent** of FastAPI and can be used It's all based on standard modern **Python** type annotations. No new syntax to learn. Just standard modern Python. -If you need a 2 minute refresher of how to use Python types (even if you don't use SQLModel or FastAPI), check the FastAPI tutorial section: [Python types intro](https://fastapi.tiangolo.com/python-types/). +If you need a 2 minute refresher on how to use Python types (even if you don't use SQLModel or FastAPI), check the FastAPI tutorial section: [Python types intro](https://fastapi.tiangolo.com/python-types/). You will also see a 20 seconds refresher on the section [Tutorial - User Guide: First Steps](tutorial/index.md). @@ -56,7 +56,7 @@ And later, you can fine-tune everything with all the power of SQLAlchemy and Pyd Underneath, ✨ a **SQLModel** model is also a **Pydantic** model. ✨ -There was a lot of research and effort dedicated to make it that way. +There was a lot of research and effort dedicated to making it that way. That means you get all of **Pydantic's features**, including automatic data **validation**, **serialization**, and **documentation**. You can use SQLModel in the same way you can use Pydantic. @@ -74,7 +74,7 @@ You will learn more about combining different models later in the tutorial. Underneath, ✨ a **SQLModel** model is also a **SQLAlchemy** model. ✨ -There was **a lot** of research and effort dedicated to make it that way. In particular, there was a lot of effort and experimentation in making a single model be **both a SQLAlchemy model and a Pydantic** model at the same time. +There was **a lot** of research and effort dedicated to making it that way. In particular, there was a lot of effort and experimentation in making a single model be **both a SQLAlchemy model and a Pydantic** model at the same time. That means that you get all the power, robustness, and certainty of SQLAlchemy, the [most widely used database library in Python](https://lp.jetbrains.com/python-developers-survey-2024/#orms). diff --git a/docs/help.md b/docs/help.md index 317c0ca35f..f76d9882f1 100644 --- a/docs/help.md +++ b/docs/help.md @@ -26,7 +26,7 @@ You can follow **FastAPI** online in several places: You can "star" SQLModel in GitHub (clicking the star button at the top right): [https://github.com/fastapi/sqlmodel](https://github.com/fastapi/sqlmodel). ⭐️ -By adding a star, other users will be able to find it more easily and see that it has been already useful for others. +By adding a star, other users will be able to find it more easily and see that it has already been useful for others. ## Watch the GitHub repository for releases diff --git a/docs/install.md b/docs/install.md index 02aed0d4fe..e8bf5d23f3 100644 --- a/docs/install.md +++ b/docs/install.md @@ -83,4 +83,4 @@ Go ahead and [Install DB Browser for SQLite](https://sqlitebrowser.org/), it's f ## Next Steps -Okay, let's get going! On the next section we'll start the [Tutorial - User Guide](tutorial/index.md). 🚀 +Okay, let's get going! In the next section we'll start the [Tutorial - User Guide](tutorial/index.md). 🚀 diff --git a/docs/tutorial/automatic-id-none-refresh.md b/docs/tutorial/automatic-id-none-refresh.md index 5687f2b42f..11029f2d8e 100644 --- a/docs/tutorial/automatic-id-none-refresh.md +++ b/docs/tutorial/automatic-id-none-refresh.md @@ -20,7 +20,7 @@ When we create a new `Hero` instance, we don't set the `id`: ### How `int | None` Helps -Because we don't set the `id`, it takes the Python's default value of `None` that we set in `Field(default=None)`. +Because we don't set the `id`, it takes Python's default value of `None` that we set in `Field(default=None)`. This is the only reason why we define it with `int | None` and with a default value of `None`. @@ -249,7 +249,7 @@ session.refresh(hero_1) ...the **session** goes and makes the **engine** communicate with the database to get the recent data for this object `hero_1`, and then the **session** puts the data in the `hero_1` object and marks it as "fresh" or "not expired". -Here's how the output would look like: +Here's what the output would look like:
@@ -442,4 +442,4 @@ If you understood all that, now you know a lot about **SQLModel**, SQLAlchemy, a If you didn't get all that, it's fine, you can always come back later to `refresh` the concepts. -I think this might be one of the main types of bugs that cause problems and makes you scratch your head. So, good job studying it! 💪 +I think this might be one of the main types of bugs that cause problems and make you scratch your head. So, good job studying it! 💪 diff --git a/docs/tutorial/code-structure.md b/docs/tutorial/code-structure.md index e9ad77f9f1..6db6217dd8 100644 --- a/docs/tutorial/code-structure.md +++ b/docs/tutorial/code-structure.md @@ -61,7 +61,7 @@ from .models import Hero, Team from .database import engine ``` -We can use these relative imports because, for example, in the file `app.py` (the `app` module) Python knows that it is **part of our Python package** because it is in the same directory as the file `__init__.py`. And all the Python files on the same directory are part of the same Python package too. +We can use these relative imports because, for example, in the file `app.py` (the `app` module) Python knows that it is **part of our Python package** because it is in the same directory as the file `__init__.py`. And all the Python files in the same directory are part of the same Python package too. ### Models File @@ -203,7 +203,7 @@ Using that trick of `TYPE_CHECKING` we can "import" the `Team` in `hero_model.py {* ./docs_src/tutorial/code_structure/tutorial002_py310/hero_model.py hl[1,5:6,16] *} -Have in mind that now we *have* to put the annotation of `Team` as a string: `"Team"`, so that Python doesn't have errors at runtime. +Keep in mind that now we *have* to put the annotation of `Team` as a string: `"Team"`, so that Python doesn't have errors at runtime. ### Team Model File @@ -221,7 +221,7 @@ Now, just for completeness, the `app.py` file would import the models from both And of course, all the tricks with `TYPE_CHECKING` and type annotations in strings are **only needed in the files with circular imports**. -As there are no circular imports with `app.py`, we can just use normal imports and use the classes as normally here. +As there are no circular imports with `app.py`, we can just use normal imports and use the classes normally here. And running that achieves the same result as before: diff --git a/docs/tutorial/connect/create-connected-tables.md b/docs/tutorial/connect/create-connected-tables.md index d1703f32bf..c18367180d 100644 --- a/docs/tutorial/connect/create-connected-tables.md +++ b/docs/tutorial/connect/create-connected-tables.md @@ -204,7 +204,7 @@ CREATE TABLE hero ( ) ``` -The only new is the `FOREIGN KEY` line, and as you can see, it tells the database what column in this table is a foreign key (`team_id`), which other (foreign) table it references (`team`) and which column in that table is the key to define which row to connect (`id`). +The only new part is the `FOREIGN KEY` line, and as you can see, it tells the database what column in this table is a foreign key (`team_id`), which other (foreign) table it references (`team`) and which column in that table is the key to define which row to connect (`id`). Feel free to experiment with it in **DB Browser for SQLite**. diff --git a/docs/tutorial/connect/read-connected-data.md b/docs/tutorial/connect/read-connected-data.md index 7432b4702e..a8bafa2c5a 100644 --- a/docs/tutorial/connect/read-connected-data.md +++ b/docs/tutorial/connect/read-connected-data.md @@ -249,7 +249,7 @@ This `JOIN` will be useful in a bit to be able to also get Spider-Boy, even if h The same way there's a `.where()` available when using `select()`, there's also a `.join()`. -And in SQLModel (actually SQLAlchemy), when using the `.join()`, because we already declared what is the `foreign_key` when creating the models, we don't have to pass an `ON` part, it is inferred automatically: +And in SQLModel (actually SQLAlchemy), we don't have to pass an `ON` part when using `.join()`. It is inferred automatically because we already declared the `foreign_key` in the model definition: {* ./docs_src/tutorial/connect/select/tutorial002_py310.py ln[61:66] hl[63] *} diff --git a/docs/tutorial/connect/update-data-connections.md b/docs/tutorial/connect/update-data-connections.md index cdce7720fe..bf168843a5 100644 --- a/docs/tutorial/connect/update-data-connections.md +++ b/docs/tutorial/connect/update-data-connections.md @@ -33,7 +33,7 @@ And a `hero` table: Some of these heroes are part of a team. -Now we'll see how to **update** those connections between rows tables. +Now we'll see how to **update** those connections between rows in tables. We will continue with the code we used to create some heroes, and we'll update them. diff --git a/docs/tutorial/create-db-and-table-with-db-browser.md b/docs/tutorial/create-db-and-table-with-db-browser.md index 508110bea5..90d4659b3c 100644 --- a/docs/tutorial/create-db-and-table-with-db-browser.md +++ b/docs/tutorial/create-db-and-table-with-db-browser.md @@ -117,7 +117,7 @@ You will see again the same table we created. ## Create the Table again, with SQL -Now, to see how is it that SQL works, let's create the table again, but with SQL. +Now, to see how SQL works, let's create the table again, but with SQL. Click the Close Database button again. @@ -151,7 +151,7 @@ You will see the "execution finished successfully" message. -And if you go back to the Database Structure tab, you will see that you effectively created again the same table. +And if you go back to the Database Structure tab, you will see that you effectively created the same table again. @@ -163,12 +163,12 @@ But if you are curious and want to get a quick overview of SQL, I recommend the 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**. 🤓 +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 in the tutorial to start being productive with **SQLModel**. 🤓 ## Recap We saw how to interact with SQLite databases in files using **DB Browser for SQLite** in a visual user interface. -We also saw how to use it to write some SQL directly to the SQLite database. This will be useful to verify the data in the database is looking correctly, to debug, etc. +We also saw how to use it to write some SQL directly to the SQLite database. This will be useful to verify the data in the database looks correct, to debug, etc. In the next chapters we will start using **SQLModel** to interact with the database, and we will continue to use **DB Browser for SQLite** at the same time to look at the database underneath. 🔍 diff --git a/docs/tutorial/create-db-and-table.md b/docs/tutorial/create-db-and-table.md index 40a9c4e1be..2900ad7367 100644 --- a/docs/tutorial/create-db-and-table.md +++ b/docs/tutorial/create-db-and-table.md @@ -65,7 +65,7 @@ And the type of each of them will also be the type of table column: {* ./docs_src/tutorial/create_db_and_table/tutorial001_py310.py ln[1:8] hl[1,5:8] *} -Let's now see with more detail these field/column declarations. +Let's now see these field/column declarations in more detail. ### `None` Fields, Nullable Columns @@ -152,7 +152,7 @@ Creating the **engine** is very simple, just call `create_engine()` with a URL f {* ./docs_src/tutorial/create_db_and_table/tutorial001_py310.py ln[1:16] hl[1,14] *} -You should normally have a single **engine** object for your whole application and re-use it everywhere. +You should normally have a single **engine** object for your whole application and reuse it everywhere. /// tip @@ -402,7 +402,7 @@ The same way, there are several possible types for storing strings. SQLite uses SQLAlchemy generates the SQL statements to create tables using `VARCHAR`, and then SQLite receives them, and internally converts them to `TEXT`s. -Additional to the difference between those two data types, some databases like MySQL require setting a maximum length for the `VARCHAR` types, for example `VARCHAR(255)` sets the maximum number of characters to 255. +In addition to the difference between those two data types, some databases like MySQL require setting a maximum length for the `VARCHAR` types, for example `VARCHAR(255)` sets the maximum number of characters to 255. 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. @@ -570,7 +570,7 @@ Review what each line does by clicking each number bubble in the code. 👆 ## Recap -We learnt how to use **SQLModel** to define how a table in the database should look like, and we created a database and a table using **SQLModel**. +We learnt how to use **SQLModel** to define what a table in the database should look like, and we created a database and a table using **SQLModel**. We also refactored the code to make it easier to reuse, share, and test later. diff --git a/docs/tutorial/delete.md b/docs/tutorial/delete.md index 87f70e7712..4ec9e1f6d4 100644 --- a/docs/tutorial/delete.md +++ b/docs/tutorial/delete.md @@ -44,7 +44,7 @@ You can try that in **DB Browser for SQLite**: -Have in mind that `DELETE` is to delete entire **rows**, not single values in a row. +Keep in mind that `DELETE` is to delete entire **rows**, not single values in a row. If you want to "delete" a single value in a column while **keeping the row**, you would instead **update** the row as explained in the previous chapter, setting the specific value of the column in that row to `NULL` (to `None` in Python). diff --git a/docs/tutorial/fastapi/multiple-models.md b/docs/tutorial/fastapi/multiple-models.md index 9de7dbb14a..d302d4028e 100644 --- a/docs/tutorial/fastapi/multiple-models.md +++ b/docs/tutorial/fastapi/multiple-models.md @@ -98,7 +98,7 @@ But we also want to have a `HeroCreate` for the data we want to receive when **c * `secret_name`, required * `age`, optional -And we want to have a `HeroPublic` with the `id` field, but this time with a type of `id: int`, instead of `id: int | None`, to make it clear that it will always have an `int` in responses **read** from the clients: +And we want to have a `HeroPublic` with the `id` field, but this time with a type of `id: int`, instead of `id: int | None`, to make it clear that it will always have an `int` in responses **read** by the clients: * `id`, required * `name`, required @@ -129,7 +129,7 @@ We will improve this code to avoid duplicating the fields, but for now we can co Let's now see how to use these new models in the FastAPI application. -Let's first check how is the process to create a hero now: +Let's first check how we create a hero now: {* ./docs_src/tutorial/fastapi/multiple_models/tutorial001_py310.py ln[44:51] hl[44:45,47] *} @@ -235,7 +235,7 @@ And those inherited fields will also be in the **autocompletion** and **inline e ### Columns and Inheritance with Multiple Models -Notice that the parent model `HeroBase` is not a **table model**, but still, we can declare `name` and `age` using `Field(index=True)`. +Notice that the parent model `HeroBase` is not a **table model**, but still, we can declare `name` and `age` using `Field(index=True)`. {* ./docs_src/tutorial/fastapi/multiple_models/tutorial002_py310.py ln[5:12] hl[6,8,11] *} diff --git a/docs/tutorial/fastapi/read-one.md b/docs/tutorial/fastapi/read-one.md index f315b2a725..fec0f711f0 100644 --- a/docs/tutorial/fastapi/read-one.md +++ b/docs/tutorial/fastapi/read-one.md @@ -24,7 +24,7 @@ For example, to get the hero with ID `2` we would send a `GET` request to: ## Handling Errors -Then, because FastAPI already takes care of making sure that the `hero_id` is an actual integer, we can use it directly with `Hero.get()` to try and get one hero by that ID. +Then, because FastAPI already takes care of making sure that the `hero_id` is an actual integer, we can use it directly with `session.get()` to try and get one hero by that ID. But if the integer is not the ID of any hero in the database, it will not find anything, and the variable `hero` will be `None`. diff --git a/docs/tutorial/fastapi/response-model.md b/docs/tutorial/fastapi/response-model.md index d2a103f7dd..029c371ad8 100644 --- a/docs/tutorial/fastapi/response-model.md +++ b/docs/tutorial/fastapi/response-model.md @@ -18,7 +18,7 @@ And it generates it **based on the code you write**, using the Pydantic models ( But up to now, the API docs UI doesn't know the schema of the *responses* our app sends back. -You can see that there's a possible "Successful Response" with a code `200`, but we have no idea how the response data would look like. +You can see that there's a possible "Successful Response" with a code `200`, but we have no idea what the response data would look like. API docs UI without response data schemas @@ -64,7 +64,7 @@ The most visible advantage of using the `response_model` is that it shows up in 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. +Additionally, because the schemas are defined using a standard, there are many tools that can take advantage of this. For example, client generators, that can automatically create the code necessary to talk to your API in many languages. diff --git a/docs/tutorial/fastapi/session-with-dependency.md b/docs/tutorial/fastapi/session-with-dependency.md index a4e6dbf8b7..11c6a4c3a4 100644 --- a/docs/tutorial/fastapi/session-with-dependency.md +++ b/docs/tutorial/fastapi/session-with-dependency.md @@ -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 [FastAPI docs about Dependencies](https://fastapi.tiangolo.com/tutorial/dependencies/). +If you want to learn more about dependencies, check out the [FastAPI docs about Dependencies](https://fastapi.tiangolo.com/tutorial/dependencies/). diff --git a/docs/tutorial/fastapi/simple-hero-api.md b/docs/tutorial/fastapi/simple-hero-api.md index 569f34643d..07417aa79c 100644 --- a/docs/tutorial/fastapi/simple-hero-api.md +++ b/docs/tutorial/fastapi/simple-hero-api.md @@ -82,7 +82,7 @@ It will be called when a user sends a request with a `POST` **operation** to the /// note -If you need a refresher on some of those concepts, checkout the FastAPI documentation: +If you need a refresher on some of those concepts, check out the FastAPI documentation: * [First Steps](https://fastapi.tiangolo.com/tutorial/first-steps/) * [Path Parameters - Data Validation and Data Conversion](https://fastapi.tiangolo.com/tutorial/path-params/) @@ -92,7 +92,7 @@ If you need a refresher on some of those concepts, checkout the FastAPI document ## The **SQLModel** Advantage -Here's where having our **SQLModel** class models be both **SQLAlchemy** models and **Pydantic** models at the same time shine. ✨ +Here's where having our **SQLModel** class models be both **SQLAlchemy** models and **Pydantic** models at the same time shines. ✨ Here we use the **same** class model to define the **request body** that will be received by our API. @@ -120,7 +120,7 @@ When a client sends a request to the **path** `/heroes/` with a `GET` HTTP **ope ## One Session per Request -Remember that we should use a SQLModel **session** per each group of operations and if we need other unrelated operations we should use a different session? +Remember that we should use a SQLModel **session** per group of operations and if we need other unrelated operations we should use a different session? Here it is much more obvious. @@ -162,7 +162,7 @@ When you use `fastapi dev` it starts Uvicorn with the option to reload automatic ## Run the **FastAPI** Server in Production Mode -The development mode should not be used in production, as it includes automatic reload by default it consumes much more resources than necessary, and it would be more error prone, etc. +The development mode should not be used in production: it includes automatic reload by default, which consumes far more resources than necessary and is more error-prone. For production, use `fastapi run` instead of `fastapi dev`: diff --git a/docs/tutorial/fastapi/teams.md b/docs/tutorial/fastapi/teams.md index 4f07fb1981..eafe4659a6 100644 --- a/docs/tutorial/fastapi/teams.md +++ b/docs/tutorial/fastapi/teams.md @@ -48,7 +48,7 @@ These are equivalent and very similar to the **path operations** for the **heroe {* ./docs_src/tutorial/fastapi/teams/tutorial001_py310.py ln[135:188] hl[135:141,144:152,155:160,163:178,181:188] *} -## Using Relationships Attributes +## Using Relationship Attributes Up to this point, we are actually not using the **relationship attributes**, but we could access them in our code. diff --git a/docs/tutorial/fastapi/tests.md b/docs/tutorial/fastapi/tests.md index b43089c368..7ec9bd9450 100644 --- a/docs/tutorial/fastapi/tests.md +++ b/docs/tutorial/fastapi/tests.md @@ -50,7 +50,7 @@ $ uv add requests pytest ## Basic Tests Code -Let's start with a simple test, with just the basic test code we need the check that the **FastAPI** application is creating a new hero correctly. +Let's start with a simple test, with just the basic test code we need to check that the **FastAPI** application is creating a new hero correctly. ```{ .python .annotate } {!./docs_src/tutorial/fastapi/app_testing/tutorial001_py310/test_main_001.py[ln:1-7]!} @@ -146,7 +146,7 @@ SQLModel.metadata.create_all(engine) 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. +In this case, it all works thanks to a little subtlety that deserves some attention. Because we import something, *anything*, from `.main`, the code in `.main` will be executed, including the definition of the **table models**, and that will automatically register them in `SQLModel.metadata`. @@ -156,13 +156,13 @@ That way, when we call `.create_all()` all the **table models** are correctly re Now we are not using the production database. Instead, we use a **new testing database** with the `testing.db` file, which is great. -But SQLite also supports having an **in memory** database. This means that all the database is only in memory, and it is never saved in a file on disk. +But SQLite also supports having an **in memory** database. This means that the whole database is only in memory, and it is never saved in a file on disk. After the program terminates, **the in-memory database is deleted**, so it wouldn't help much for a production database. But **it works great for testing**, because it can be quickly created before each test, and quickly removed after each test. ✅ -And also, because it never has to write anything to a file and it's all just in memory, it will be even faster than normally. 🏎 +And also, because it never has to write anything to a file and it's all just in memory, it will be even faster than a disk-based database. 🏎 /// details | Other alternatives and ideas 👀 @@ -222,11 +222,11 @@ In fact, pytest was one of the things that inspired the design of the dependenci 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). -In fact, it also has the same trick of allowing to use `yield` instead of `return` to provide the value, and then **pytest** makes sure that the code after `yield` is executed *after* the function with the test is done. +In fact, it also has the same trick of allowing you to use `yield` instead of `return` to provide the value, and then **pytest** makes sure that the code after `yield` is executed *after* the function with the test is done. In pytest, these things are called **fixtures** instead of *dependencies*. -Let's use these **fixtures** to improve our code and reduce de duplicated boilerplate for the next tests. +Let's use these **fixtures** to improve our code and reduce the duplicated boilerplate for the next tests. ## Pytest Fixtures @@ -253,7 +253,7 @@ Check out the number bubbles to see what is done by each line of code. But apart from the way we declare them and how we tell the framework that we want to have them in the function, they **work in a very similar way**. -Now we create lot's of tests and re-use that same fixture in all of them, saving us that **boilerplate code**. +Now we create lots of tests and reuse that same fixture in all of them, saving us that **boilerplate code**. **pytest** will make sure to run them right before (and finish them right after) each test function. So, each test function will actually have its own database, engine, and session. @@ -302,13 +302,13 @@ Let's add some more tests: /// 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. +It's always a **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. /// -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! 😎 +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 set up. Nice! 😎 ## Why Two Fixtures diff --git a/docs/tutorial/fastapi/update-extra-data.md b/docs/tutorial/fastapi/update-extra-data.md index 74b24d4afa..bff57784f6 100644 --- a/docs/tutorial/fastapi/update-extra-data.md +++ b/docs/tutorial/fastapi/update-extra-data.md @@ -10,7 +10,7 @@ This is particularly useful when you need to **generate some data** in your code Let's imagine that each hero in our system also has a **password**. -We should never store the password in plain text in the database, we should only stored a **hashed version** of it. +We should never store the password in plain text in the database, we should only store a **hashed version** of it. "**Hashing**" means converting some content (a password in this case) into a sequence of bytes (just a string) that looks like gibberish. diff --git a/docs/tutorial/indexes.md b/docs/tutorial/indexes.md index 45a74a40c5..2a411671a6 100644 --- a/docs/tutorial/indexes.md +++ b/docs/tutorial/indexes.md @@ -52,7 +52,7 @@ You know that `d` from `database` comes before `f`. So it has to be **before** t -Now you have a **small section of dictionary** to search (only a **quarter** of dictionary can have your word). You take that **quarter** of the pages at the start of the dictionary that can contain your word, and open it in the middle of that section. Maybe you arrive at the letter `c`. +Now you have a **small section of dictionary** to search (only a **quarter** of the dictionary can have your word). You take that **quarter** of the pages at the start of the dictionary that can contain your word, and open it in the middle of that section. Maybe you arrive at the letter `c`. @@ -90,7 +90,7 @@ Now let's imagine you are reading a technical book. For example, with several to This book might have a **book index**: a section in the book that has some **names of topics covered** and the **page numbers** in the book where you can read about them. And the topic names are **sorted** in alphabetic order, pretty much like a dictionary (a book with words, as in the previous example). -In this case, you can open that book in the end (or in the beginning) to find the **book index** section, it would have only a few pages. And then, you can do the same process as with the **dictionary** example above. +In this case, you can open that book at the end (or at the beginning) to find the **book index** section, it would have only a few pages. And then, you can do the same process as with the **dictionary** example above. Open the index, and after **5 or 10 steps**, quickly find the topic "**database**" with the page numbers where that is covered, for example "page 253 in Chapter 5". Now you used the dictionary technique to find the **topic**, and that topic gave you a **page number**. diff --git a/docs/tutorial/insert.md b/docs/tutorial/insert.md index 93028518f5..4f71d7873c 100644 --- a/docs/tutorial/insert.md +++ b/docs/tutorial/insert.md @@ -2,7 +2,7 @@ Now that we have a database and a table, we can start adding data. -Here's a reminder of how the table would look like, this is the data we want to add: +Here's a reminder of what the table would look like, this is the data we want to add: @@ -39,7 +39,7 @@ This is the code we had to create the database and table, nothing new here: //// -Now that we can create the database and the table, we will continue from this point and add more code on the same file to create the data. +Now that we can create the database and the table, we will continue from this point and add more code in the same file to create the data. ## Create Data with SQL @@ -68,9 +68,9 @@ It means, more or less: > * `"Deadpond"` > * `"Dive Wilson"` -### Try it in DB Explorer for SQLite +### Try it in DB Browser for SQLite -You can try that SQL statement in **DB Explorer for SQLite**. +You can try that SQL statement in **DB Browser for SQLite**. Make sure to open the same database we already created by clicking Open Database and selecting the same `database.db` file. @@ -159,7 +159,7 @@ In fact, the **session** needs and uses an **engine**. For example, if we have a web application, we would normally have a single **session** per request. -We would re-use the same **engine** in all the code, everywhere in the application (shared by all the requests). But for each request, we would create and use a new **session**. And once the request is done, we would close the session. +We would reuse the same **engine** in all the code, everywhere in the application (shared by all the requests). But for each request, we would create and use a new **session**. And once the request is done, we would close the session. The first step is to import the `Session` class: @@ -335,7 +335,7 @@ 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. +You can now put it in an `app.py` file and run it with Python. And you will see an output like the one shown above. After that, if you open the database with **DB Browser for SQLite**, you will see the data you just created in the Browse Data tab: diff --git a/docs/tutorial/limit-and-offset.md b/docs/tutorial/limit-and-offset.md index 97c9d1decf..f00e416f48 100644 --- a/docs/tutorial/limit-and-offset.md +++ b/docs/tutorial/limit-and-offset.md @@ -95,7 +95,7 @@ We can use `.offset()`: The way this works is that the special **select** object we get from `select()` has methods like `.where()`, `.offset()` and `.limit()`. -Each of those methods applies the change in the internal special select statement object, and also **return the same object**, this way, we can continue using more methods on it, like in the example above that we use both `.offset()` and `.limit()`. +Each of those methods applies the change in the internal special select statement object, and also **returns the same object**, this way, we can continue using more methods on it, like in the example above that we use both `.offset()` and `.limit()`. **Offset** means "skip this many rows", and as we want to skip the ones we already saw, the first three, we use `.offset(3)`. @@ -106,7 +106,7 @@ Now we can run the program on the command line, and it will output:
```console -$python app.py +$ python app.py // Previous output omitted 🙈 diff --git a/docs/tutorial/many-to-many/create-data.md b/docs/tutorial/many-to-many/create-data.md index e434895bf6..c275ba217d 100644 --- a/docs/tutorial/many-to-many/create-data.md +++ b/docs/tutorial/many-to-many/create-data.md @@ -1,6 +1,6 @@ # Create Data with Many-to-Many Relationships -Let's continue from where we left and create some data. +Let's continue from where we left off and create some data. We'll create data for this same **many-to-many** relationship with a link table: @@ -61,7 +61,7 @@ INFO Engine INSERT INTO team (name, headquarters) VALUES (?, ?) INFO Engine [generated in 0.00037s] ('Z-Force', 'Sister Margaret's Bar') INFO Engine INSERT INTO team (name, headquarters) VALUES (?, ?) INFO Engine [cached since 0.001239s ago] ('Preventers', 'Sharp Tower') -// Insert the link data last, to be able to re-use the created IDs +// Insert the link data last, to be able to reuse the created IDs INFO Engine INSERT INTO heroteamlink (team_id, hero_id) VALUES (?, ?) INFO Engine [generated in 0.00026s] ((2, 3), (1, 1), (2, 1), (2, 2)) // Commit and save the data in the database @@ -119,7 +119,7 @@ INFO Engine [cached since 0.002739s ago] (3,) // Print Spider-Boy's teams, just one, but still a list Spider-Boy Teams: [Team(id=2, name='Preventers', headquarters='Sharp Tower')] -// Automatic roll back any previous automatic transaction, at the end of the with block +// Automatically roll back any previous automatic transaction, at the end of the with block INFO Engine ROLLBACK ``` diff --git a/docs/tutorial/many-to-many/create-models-with-link.md b/docs/tutorial/many-to-many/create-models-with-link.md index 9822474205..34fbd3b2f9 100644 --- a/docs/tutorial/many-to-many/create-models-with-link.md +++ b/docs/tutorial/many-to-many/create-models-with-link.md @@ -24,7 +24,7 @@ And **both fields are primary keys**. We hadn't used this before. 🤓 ## Team Model -Let's see the `Team` model, it's almost identical as before, but with a little change: +Let's see the `Team` model, it's almost identical to the previous version, but with a little change: {* ./docs_src/tutorial/many_to_many/tutorial001_py310.py ln[9:14] hl[14] *} @@ -98,7 +98,7 @@ CREATE TABLE hero ( INFO Engine [no key 0.00016s] () INFO Engine -// Our shinny new link table ✨ +// Our shiny new link table ✨ CREATE TABLE heroteamlink ( team_id INTEGER, hero_id INTEGER, diff --git a/docs/tutorial/many-to-many/index.md b/docs/tutorial/many-to-many/index.md index 12b04e602a..908e028001 100644 --- a/docs/tutorial/many-to-many/index.md +++ b/docs/tutorial/many-to-many/index.md @@ -132,7 +132,7 @@ Cool, we have a link table with **just two columns**. But remember that SQL data Now, what is the **primary key** in this table? -How to we identify each unique row? +How do we identify each unique row? Should we add another column just to be the **primary key** of this link table? Nope! We don't have to do that. 👌 diff --git a/docs/tutorial/many-to-many/link-with-extra-fields.md b/docs/tutorial/many-to-many/link-with-extra-fields.md index f6fb8f544f..b44dbb5ebe 100644 --- a/docs/tutorial/many-to-many/link-with-extra-fields.md +++ b/docs/tutorial/many-to-many/link-with-extra-fields.md @@ -59,7 +59,7 @@ We no longer have the `heroes` relationship attribute, and instead we have the n The same with the `Hero` model. -We change the `teams` relationship attribute for `team_links`: +We change the `teams` relationship attribute to `team_links`: {* ./docs_src/tutorial/many_to_many/tutorial003_py310.py ln[21:27] hl[27] *} @@ -71,7 +71,7 @@ But now we create the **explicit link models** manually, pointing to their hero {* ./docs_src/tutorial/many_to_many/tutorial003_py310.py ln[40:79] hl[58:67,69:72] *} -We are just adding the link model instances to the session, because the link model instances are connected to the heroes and teams, they will be also automatically included in the session when we commit. +We are just adding the link model instances to the session, because the link model instances are connected to the heroes and teams, they will also be automatically included in the session when we commit. ## Run the Program diff --git a/docs/tutorial/many-to-many/update-remove-relationships.md b/docs/tutorial/many-to-many/update-remove-relationships.md index 116ba223a5..a2a15aa645 100644 --- a/docs/tutorial/many-to-many/update-remove-relationships.md +++ b/docs/tutorial/many-to-many/update-remove-relationships.md @@ -12,7 +12,7 @@ Let's now create a function `update_heroes()`. We'll get **Spider-Boy** and the **Z-Force** team. -As you already know how these goes, I'll use the **short version** and get the data in a single Python statement. +As you already know how this goes, I'll use the **short version** and get the data in a single Python statement. And because we are now using `select()`, we also have to import it. @@ -38,7 +38,7 @@ 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. +We then commit the change, refresh, and print the updated **Spider-Boy**'s teams to confirm. Notice that we only `add` **Z-Force** to the session, then we commit. @@ -81,7 +81,7 @@ Updated Spider-Boy's Teams: [ Team(id=1, name='Z-Force', headquarters='Sister Margaret's Bar') ] -// Automatically refresh the data while accessing the attribute .heores +// Automatically refresh the data while accessing the attribute .heroes INFO Engine SELECT hero.id AS hero_id, hero.name AS hero_name, hero.secret_name AS hero_secret_name, hero.age AS hero_age FROM hero, heroteamlink WHERE ? = heroteamlink.team_id AND hero.id = heroteamlink.hero_id @@ -115,7 +115,7 @@ In this case, we use the method `.remove()`, that takes an item and removes it f {* ./docs_src/tutorial/many_to_many/tutorial002_py310.py ln[72:91] hl[86:88,90:91] *} -And this time, just to show again that by using `back_populates` **SQLModel** (actually SQLAlchemy) takes care of connecting the models by their relationships, even though we performed the operation from the `hero_spider_boy` object (modifying `hero_spider_boy.teams`), we are adding `team_z_force` to the **session**. And we commit that, without even add `hero_spider_boy`. +And this time, just to show again that by using `back_populates` **SQLModel** (actually SQLAlchemy) takes care of connecting the models by their relationships, even though we performed the operation from the `hero_spider_boy` object (modifying `hero_spider_boy.teams`), we are adding `team_z_force` to the **session**. And we commit that, without even adding `hero_spider_boy`. This still works because by updating the teams in `hero_spider_boy`, because they are synchronized with `back_populates`, the changes are also reflected in `team_z_force`, so it also has changes to be saved in the DB (that **Spider-Boy** was removed). @@ -184,4 +184,4 @@ INFO Engine ROLLBACK Updating and removing many-to-many relationships is quite straightforward after setting up the **link model** and the relationship attributes. -You can just use common list operation. 🚀 +You can just use common list operations. 🚀 diff --git a/docs/tutorial/one.md b/docs/tutorial/one.md index 733d2a5e24..ad181247f0 100644 --- a/docs/tutorial/one.md +++ b/docs/tutorial/one.md @@ -88,7 +88,7 @@ FROM hero WHERE hero.age <= ? INFO Engine [no key 0.00021s] (35,) -// Now rows found, first is None +// No rows found, first is None Hero: None ``` @@ -219,7 +219,7 @@ As selecting a single row by its Id column with the **primary key** is a common {* ./docs_src/tutorial/one/tutorial008_py310.py ln[42:45] hl[44] *} -`session.get(Hero, 1)` is an equivalent to creating a `select()`, then filtering by Id using `.where()`, and then getting the first item with `.first()`. +`session.get(Hero, 1)` is equivalent to creating a `select()`, then filtering by Id using `.where()`, and then getting the first item with `.first()`. If you run it, it will output: @@ -244,7 +244,7 @@ Hero: secret_name='Dive Wilson' age=None id=1 name='Deadpond' ## Select by Id with `.get()` with No Data -`.get()` behaves similar to `.first()`, if there's no data it will simply return `None` (instead of raising an error): +`.get()` behaves similarly to `.first()`, if there's no data it will simply return `None` (instead of raising an error): {* ./docs_src/tutorial/one/tutorial009_py310.py ln[42:45] hl[44] *} diff --git a/docs/tutorial/relationship-attributes/back-populates.md b/docs/tutorial/relationship-attributes/back-populates.md index 9bdcbf0c28..63082c6132 100644 --- a/docs/tutorial/relationship-attributes/back-populates.md +++ b/docs/tutorial/relationship-attributes/back-populates.md @@ -26,7 +26,7 @@ Let's see how that works by writing an **incomplete** version first, without `ba Now, we will get the **Spider-Boy** hero and, *independently*, the **Preventers** team using two `select`s. -As you already know how this works, I won't separate that in a select `statement`, `results`, etc. Let's use the shorter form in a single call: +As you already know how this works, I won't separate that into a select `statement`, `results`, etc. Let's use the shorter form in a single call: {* ./docs_src/tutorial/relationship_attributes/back_populates/tutorial001_py310.py ln[103:111] hl[105:107,109:111] *} @@ -66,7 +66,7 @@ Now let's update **Spider-Boy**, removing him from the team by setting `hero_spi The first important thing is, we *haven't committed* the hero yet, so accessing the list of heroes would not trigger an automatic refresh. -But in our code, in this exact point in time, we already said that **Spider-Boy** is no longer part of the **Preventers**. 🔥 +But in our code, at this exact point in time, we already said that **Spider-Boy** is no longer part of the **Preventers**. 🔥 /// tip diff --git a/docs/tutorial/relationship-attributes/cascade-delete-relationships.md b/docs/tutorial/relationship-attributes/cascade-delete-relationships.md index e3ff59bac1..39d980db83 100644 --- a/docs/tutorial/relationship-attributes/cascade-delete-relationships.md +++ b/docs/tutorial/relationship-attributes/cascade-delete-relationships.md @@ -30,7 +30,7 @@ Let's say that we have these **teams** and **heroes**. | id | name | secret_name | age | team_id | | ---- | --------------- | ---------------- | ---- | ------- | -| 1 | Deadpond | Dive WIlson | | 1 | +| 1 | Deadpond | Dive Wilson | | 1 | | 2 | Rusty-Man | Tommy Sharp | 48 | 2 | | 3 | Spider-Boy | Pedro Parqueador | | 2 | | 4 | Black Lion | Trevor Challa | 35 | 3 | @@ -648,4 +648,4 @@ In some cases, when you want to **cascade** the delete of a record to its relate That will **cover most of the use cases**. 🚀 -And if you need something else, you can refer the additional options described above. 🤓 +And if you need something else, you can refer to the additional options described above. 🤓 diff --git a/docs/tutorial/relationship-attributes/define-relationships-attributes.md b/docs/tutorial/relationship-attributes/define-relationships-attributes.md index c5307d3663..5368a82297 100644 --- a/docs/tutorial/relationship-attributes/define-relationships-attributes.md +++ b/docs/tutorial/relationship-attributes/define-relationships-attributes.md @@ -1,4 +1,4 @@ -# Define Relationships Attributes +# Define Relationship Attributes Now we are finally in one of the most exciting parts of **SQLModel**. diff --git a/docs/tutorial/relationship-attributes/index.md b/docs/tutorial/relationship-attributes/index.md index c45f75b4ee..a1427fb1a0 100644 --- a/docs/tutorial/relationship-attributes/index.md +++ b/docs/tutorial/relationship-attributes/index.md @@ -10,7 +10,7 @@ Now we will see how to use **Relationship Attributes**, an extra feature of **SQ 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. +I'm not using the term "**relation**" that is the technical, academic, SQL term for a single table. /// diff --git a/docs/tutorial/relationship-attributes/read-relationships.md b/docs/tutorial/relationship-attributes/read-relationships.md index aa783f45d2..cfa77d92e9 100644 --- a/docs/tutorial/relationship-attributes/read-relationships.md +++ b/docs/tutorial/relationship-attributes/read-relationships.md @@ -1,6 +1,6 @@ # Read Relationships -Now that we know how to connect data using **relationship Attributes**, let's see how to get and read the objects from a relationship. +Now that we know how to connect data using **relationship attributes**, let's see how to get and read the objects from a relationship. ## Select a Hero diff --git a/docs/tutorial/select.md b/docs/tutorial/select.md index df0d1c0ff6..89a900626e 100644 --- a/docs/tutorial/select.md +++ b/docs/tutorial/select.md @@ -31,7 +31,7 @@ We will need the `Hero` model and the **engine**, but we will create a new sessi ## Read Data with SQL -Before writing Python code let's do a quick review of how querying data with SQL looks like: +Before writing Python code let's do a quick review of what querying data with SQL looks like: ```SQL SELECT id, name, secret_name, age @@ -259,7 +259,7 @@ Now include a call to `select_heroes()` in the `main()` function so that it is e ## Review The Code -Great, you're now being able to read the data from the database! 🎉 +Great, you're now able to read the data from the database! 🎉 Let's review the code up to this point: @@ -293,7 +293,7 @@ So, both sections could be in **different places** and would need their own sess 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 group of operations. /// @@ -329,7 +329,7 @@ It would actually look more compact, I'm formatting it a bit for you to see that I have been creating several variables to be able to explain to you what each thing is doing. -But knowing what is each object and what it is all doing, we can simplify it a bit and put it in a more compact form: +But knowing what each object is and what it is all doing, we can simplify it a bit and put it in a more compact form: {* ./docs_src/tutorial/select/tutorial004_py310.py ln[34:37] hl[36] *} @@ -349,7 +349,7 @@ In this chapter we are touching some of them. When importing from `sqlmodel` the `select()` function, you are using **SQLModel**'s version of `select`. -SQLAchemy also has its own `select`, and SQLModel's `select` uses SQLAlchemy's `select` internally. +SQLAlchemy also has its own `select`, and SQLModel's `select` uses SQLAlchemy's `select` internally. 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. ✨ @@ -369,7 +369,7 @@ If you see SQLAlchemy tutorials, they will always use `session.execute()`. **SQLModel**'s own `Session` inherits directly from SQLAlchemy's `Session`, and adds this additional method `session.exec()`. Underneath, it uses the same `session.execute()`. -But `session.exec()` does several **tricks** combined with the tricks in `session()` to give you the **best editor support**, with **autocompletion** and **inline errors** everywhere, even after getting data from a select. ✨ +But `session.exec()` does several **tricks** combined with the tricks in `select()` to give you the **best editor support**, with **autocompletion** and **inline errors** everywhere, even after getting data from a select. ✨ For example, in SQLAlchemy you would need to add a `.scalars()` here: diff --git a/docs/tutorial/where.md b/docs/tutorial/where.md index 1e33244b3d..296751c018 100644 --- a/docs/tutorial/where.md +++ b/docs/tutorial/where.md @@ -90,7 +90,7 @@ You can try that out in **DB Browser for SQLite**: These additional keywords with some sections like `WHERE` and `FROM` that go after `SELECT` (or others) have a technical name, they are called **clauses**. -There are others clauses too, with their own SQL keywords. +There are other clauses too, with their own SQL keywords. I won't use the term **clause** too much here, but it's good for you to know it as it will probably show up in other tutorials you could study later. 🤓 @@ -101,7 +101,7 @@ Here's a quick tip that helps me think about it. * **`SELECT`** is used to tell the SQL database what **columns** to return. * **`WHERE`** is used to tell the SQL database what **rows** to return. -The size of the table in the two dimensions depend mostly on those two keywords. +The size of the table in the two dimensions depends mostly on those two keywords. ### `SELECT` Land @@ -181,7 +181,7 @@ Starting with some table: Let's review some of the code we used to read data with **SQLModel**. -We care specially about the **select** statement: +We care especially about the **select** statement: {* ./docs_src/tutorial/select/tutorial001_py310.py ln[34:39] hl[36] *} @@ -191,7 +191,7 @@ Now, the same way that we add `WHERE` to a SQL statement to filter rows, we can {* ./docs_src/tutorial/where/tutorial001_py310.py ln[34:39] hl[36] *} -It's a very small change, but it's packed of details. Let's explore them. +It's a very small change, but it's packed with details. Let's explore them. ## `select()` Objects @@ -220,7 +220,7 @@ Notice that we didn't call it with a single equal (`=`) sign, and with something select(Hero).where(name="Deadpond") ``` -That would have been shorter, of course, but it would have been much more error prone and limited. I'll show you why in a bit. +That would have been shorter, of course, but it would have been much more error-prone and limited. I'll show you why in a bit. Instead, we used two `==`: @@ -474,7 +474,7 @@ We'll see other ways to get the data later. ## Other Comparisons -Here's another great advantage of these special **expressions** passed to `.where()`. +Here's another great advantage of these special **expressions** passed to `.where()`. Above, we have been using an "equality" comparison (using `==`), only checking if two things are the same value. @@ -538,7 +538,7 @@ 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`. +This time we got `Black Lion` too because although the age is not *strictly* greater than `35` it is *equal* to `35`. /// @@ -548,7 +548,7 @@ Similarly, we can use `<` to get the rows where a column is **less than** a valu {* ./docs_src/tutorial/where/tutorial005_py310.py ln[42:47] hl[44] *} -And we get the younger one with an age in the database: +And we get the youngest one with an age in the database: ``` age=32 id=4 name='Tarantula' secret_name='Natalia Roman-on' @@ -585,7 +585,7 @@ Finally, we can use `in_` to get the rows where a column is a member of a collec {* ./docs_src/tutorial/where/tutorial006b_py310.py ln[42:47] hl[44] *} -In this case, we match `Deadpond` since it's part of the collections of names. +In this case, we match `Deadpond` since it's part of the collection of names. We don't have any hero called `Ratman`, so it does not match any hero. /// tip @@ -708,13 +708,13 @@ It would be an error telling you that This is because as we are using pure and plain Python annotations for the fields, `age` is indeed annotated as `int | None`. -By using this simple and standard Python type annotations we get the benefit of the extra simplicity and the inline error checks when creating or using instances. ✨ +By using these simple and standard Python type annotations we get the benefit of the extra simplicity and the inline error checks when creating or using instances. ✨ And when we use these special **class attributes** in a `.where()`, during execution of the program, the special class attribute will know that the comparison only applies for the values that are not `NULL` in the database, and it will work correctly. -But the editor doesn't know that it's a special **class attribute**, so it tries to help us preventing an error (that in this case is a false alarm). +But the editor doesn't know that it's a special **class attribute**, so it tries to help us prevent an error (that in this case is a false alarm). -Nevertheless, we can easily fix. 🎉 +Nevertheless, we can easily fix it. 🎉 We can tell the editor that this class attribute is actually a special **SQLModel** column (instead of an instance attribute with a normal value). @@ -742,7 +742,7 @@ And with that the editor knows this code is actually fine, because this is a spe /// tip -That `col()` will come handy later, giving autocompletion to several other things we can do with these special **class attributes** for columns. +That `col()` will come in handy later, giving autocompletion to several other things we can do with these special **class attributes** for columns. But we'll get there later. diff --git a/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/annotations/en/test_main_005.md b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/annotations/en/test_main_005.md index 126e1f1790..e361871455 100644 --- a/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/annotations/en/test_main_005.md +++ b/docs_src/tutorial/fastapi/app_testing/tutorial001_py310/annotations/en/test_main_005.md @@ -38,4 +38,4 @@ 6. Now in the dependency override function, we just return the same `session` object that came from outside it. - The `session` object comes from the parameter passed to the test function, and we just re-use it and return it here in the dependency override. + The `session` object comes from the parameter passed to the test function, and we just reuse it and return it here in the dependency override. diff --git a/scripts/docs.py b/scripts/docs.py index a504243f2e..ec98067307 100644 --- a/scripts/docs.py +++ b/scripts/docs.py @@ -99,13 +99,7 @@ def verify_readme() -> None: @app.command() def live(dirty: bool = False) -> None: """ - Serve with livereload a docs site for a specific language. - - This only shows the actual translated files, not the placeholders created with - build-all. - - Takes an optional LANG argument with the name of the language to serve, by default - en. + Serve the docs site with livereload, at http://127.0.0.1:8008. """ # Enable line numbers during local development to make it easier to highlight args = ["zensical", "serve", "--dev-addr", "127.0.0.1:8008"] diff --git a/sqlmodel/_compat.py b/sqlmodel/_compat.py index a220b193f1..dc516047f1 100644 --- a/sqlmodel/_compat.py +++ b/sqlmodel/_compat.py @@ -184,7 +184,7 @@ def get_sa_type_from_type_annotation(annotation: Any) -> Any: bases = get_args(annotation) if len(bases) > 2: raise ValueError("Cannot have a (non-optional) union as a SQLAlchemy field") - # Non optional unions are not allowed + # Non-optional unions are not allowed if bases[0] is not NoneType and bases[1] is not NoneType: raise ValueError("Cannot have a (non-optional) union as a SQLAlchemy field") # Optional unions are allowed diff --git a/sqlmodel/ext/asyncio/session.py b/sqlmodel/ext/asyncio/session.py index f2b989e2fa..6df870bc61 100644 --- a/sqlmodel/ext/asyncio/session.py +++ b/sqlmodel/ext/asyncio/session.py @@ -105,7 +105,7 @@ async def exec( 🚨 You probably want to use `session.exec()` instead of `session.execute()`. This is the original SQLAlchemy `session.execute()` method that returns objects - of type `Row`, and that you have to call `scalars()` to get the model objects. + of type `Row`, on which you have to call `scalars()` to get the model objects. For example: @@ -114,7 +114,7 @@ async def exec( heroes = result.scalars().all() ``` - instead you could use `exec()`: + Instead, you could use `exec()`: ```Python result = await session.exec(select(Hero)) @@ -136,7 +136,7 @@ async def execute( 🚨 You probably want to use `session.exec()` instead of `session.execute()`. This is the original SQLAlchemy `session.execute()` method that returns objects - of type `Row`, and that you have to call `scalars()` to get the model objects. + of type `Row`, on which you have to call `scalars()` to get the model objects. For example: @@ -145,7 +145,7 @@ async def execute( heroes = result.scalars().all() ``` - instead you could use `exec()`: + Instead, you could use `exec()`: ```Python result = await session.exec(select(Hero)) diff --git a/sqlmodel/main.py b/sqlmodel/main.py index c551afea36..822095e3c1 100644 --- a/sqlmodel/main.py +++ b/sqlmodel/main.py @@ -1006,6 +1006,6 @@ def sqlmodel_update( else: raise ValueError( "Can't use sqlmodel_update() with something that " - f"is not a dict or SQLModel or Pydantic model: {obj}" + f"is not a dict, SQLModel, or Pydantic model: {obj}" ) return self diff --git a/sqlmodel/orm/session.py b/sqlmodel/orm/session.py index 5c721ae0da..d49d0aad77 100644 --- a/sqlmodel/orm/session.py +++ b/sqlmodel/orm/session.py @@ -89,7 +89,7 @@ def exec( 🚨 You probably want to use `session.exec()` instead of `session.execute()`. This is the original SQLAlchemy `session.execute()` method that returns objects - of type `Row`, and that you have to call `scalars()` to get the model objects. + of type `Row`, on which you have to call `scalars()` to get the model objects. For example: @@ -97,7 +97,7 @@ def exec( heroes = session.execute(select(Hero)).scalars().all() ``` - instead you could use `exec()`: + Instead, you could use `exec()`: ```Python heroes = session.exec(select(Hero)).all() @@ -119,7 +119,7 @@ def execute( 🚨 You probably want to use `session.exec()` instead of `session.execute()`. This is the original SQLAlchemy `session.execute()` method that returns objects - of type `Row`, and that you have to call `scalars()` to get the model objects. + of type `Row`, on which you have to call `scalars()` to get the model objects. For example: @@ -127,7 +127,7 @@ def execute( heroes = session.execute(select(Hero)).scalars().all() ``` - instead you could use `exec()`: + Instead, you could use `exec()`: ```Python heroes = session.exec(select(Hero)).all() @@ -146,10 +146,10 @@ def execute( """ 🚨 You probably want to use `session.exec()` instead of `session.query()`. - `session.exec()` is SQLModel's own short version with increased type + `session.exec()` is SQLModel's own short version with improved type annotations. - Or otherwise you might want to use `session.execute()` instead of + Otherwise, you might want to use `session.execute()` instead of `session.query()`. """ ) @@ -159,10 +159,10 @@ def query( # type: ignore """ 🚨 You probably want to use `session.exec()` instead of `session.query()`. - `session.exec()` is SQLModel's own short version with increased type + `session.exec()` is SQLModel's own short version with improved type annotations. - Or otherwise you might want to use `session.execute()` instead of + Otherwise, you might want to use `session.execute()` instead of `session.query()`. """ return super().query(*entities, **kwargs)