mirror of
https://github.com/PaiGramTeam/sqlmodel.git
synced 2024-11-30 02:44:13 +00:00
430 lines
9.5 KiB
Markdown
430 lines
9.5 KiB
Markdown
|
# Read One Row
|
||
|
|
||
|
You already know how to filter rows to select using `.where()`.
|
||
|
|
||
|
And you saw how when executing a `select()` it normally returns an **iterable** object.
|
||
|
|
||
|
Or you can call `results.all()` to get a **list** of all the rows right away, instead of an iterable.
|
||
|
|
||
|
But in many cases you really just want to read a **single row**, and having to deal with an iterable or a list is not as convenient.
|
||
|
|
||
|
Let's see the utilities to read a single row.
|
||
|
|
||
|
## Continue From Previous Code
|
||
|
|
||
|
We'll continue with the same examples we have been using in the previous chapters to create and select data and we'll keep udpating them.
|
||
|
|
||
|
<details>
|
||
|
<summary>👀 Full file preview</summary>
|
||
|
|
||
|
```Python
|
||
|
{!./docs_src/tutorial/where/tutorial006.py!}
|
||
|
```
|
||
|
|
||
|
</details>
|
||
|
|
||
|
If you already executed the previous examples and have a database with data, **remove the database file** before running each example, that way you won't have duplicate data and you will be able to get the same results.
|
||
|
|
||
|
## Read the First Row
|
||
|
|
||
|
We have been iterating over the rows in a `result` object like:
|
||
|
|
||
|
```Python hl_lines="7-8"
|
||
|
# Code above omitted 👆
|
||
|
|
||
|
{!./docs_src/tutorial/where/tutorial006.py[ln:44-49]!}
|
||
|
|
||
|
# Code below omitted 👇
|
||
|
```
|
||
|
|
||
|
<details>
|
||
|
<summary>👀 Full file preview</summary>
|
||
|
|
||
|
```Python
|
||
|
{!./docs_src/tutorial/where/tutorial006.py!}
|
||
|
```
|
||
|
|
||
|
</details>
|
||
|
|
||
|
But let's say that we are not interested in all the rows, just the **first** one.
|
||
|
|
||
|
We can call the `.first()` method on the `results` object to get the first row:
|
||
|
|
||
|
```Python hl_lines="7"
|
||
|
# Code above omitted 👆
|
||
|
|
||
|
{!./docs_src/tutorial/one/tutorial001.py[ln:44-49]!}
|
||
|
|
||
|
# Code below omitted 👇
|
||
|
```
|
||
|
|
||
|
<details>
|
||
|
<summary>👀 Full file preview</summary>
|
||
|
|
||
|
```Python
|
||
|
{!./docs_src/tutorial/one/tutorial001.py!}
|
||
|
```
|
||
|
|
||
|
</details>
|
||
|
|
||
|
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.
|
||
|
|
||
|
Although this query would find two rows, by using `.first()` we get only the first row.
|
||
|
|
||
|
If we run it in the command line it would output:
|
||
|
|
||
|
<div class="termy">
|
||
|
|
||
|
```console
|
||
|
$ python app.py
|
||
|
|
||
|
// Some boilerplate output omitted 😉
|
||
|
|
||
|
// The SELECT with WHERE
|
||
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
||
|
FROM hero
|
||
|
WHERE hero.age <= ?
|
||
|
INFO Engine [no key 0.00021s] (35,)
|
||
|
|
||
|
// Only print the first item
|
||
|
Hero: secret_name='Natalia Roman-on' age=32 id=4 name='Tarantula'
|
||
|
```
|
||
|
|
||
|
</div>
|
||
|
|
||
|
## First or `None`
|
||
|
|
||
|
It would be possible that the SQL query doesn't find any row.
|
||
|
|
||
|
In that case, `.first()` will return `None`:
|
||
|
|
||
|
```Python hl_lines="5 7"
|
||
|
# Code above omitted 👆
|
||
|
|
||
|
{!./docs_src/tutorial/one/tutorial002.py[ln:44-49]!}
|
||
|
|
||
|
# Code below omitted 👇
|
||
|
```
|
||
|
|
||
|
<details>
|
||
|
<summary>👀 Full file preview</summary>
|
||
|
|
||
|
```Python
|
||
|
{!./docs_src/tutorial/one/tutorial002.py!}
|
||
|
```
|
||
|
|
||
|
</details>
|
||
|
|
||
|
In this case, as there's no hero with an age less than 25, `.first()` will return `None`.
|
||
|
|
||
|
When we run it in the command line it will output:
|
||
|
|
||
|
<div class="termy">
|
||
|
|
||
|
```console
|
||
|
$ python app.py
|
||
|
|
||
|
// Some boilerplate output omitted 😉
|
||
|
|
||
|
// The SELECT with WHERE
|
||
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
||
|
FROM hero
|
||
|
WHERE hero.age <= ?
|
||
|
INFO Engine [no key 0.00021s] (35,)
|
||
|
|
||
|
// Now rows found, first is None
|
||
|
Hero: None
|
||
|
```
|
||
|
|
||
|
</div>
|
||
|
|
||
|
## Exactly One
|
||
|
|
||
|
There might be cases where we want to ensure that there's exactly **one** row matching the query.
|
||
|
|
||
|
And if there was more than one, it would mean that there's an error in the system, and we should terminate with an error.
|
||
|
|
||
|
In that case, instead of `.first()` we can use `.one()`:
|
||
|
|
||
|
```Python hl_lines="7"
|
||
|
# Code above omitted 👆
|
||
|
|
||
|
{!./docs_src/tutorial/one/tutorial003.py[ln:44-49]!}
|
||
|
|
||
|
# Code below omitted 👇
|
||
|
```
|
||
|
|
||
|
<details>
|
||
|
<summary>👀 Full file preview</summary>
|
||
|
|
||
|
```Python
|
||
|
{!./docs_src/tutorial/one/tutorial003.py!}
|
||
|
```
|
||
|
|
||
|
</details>
|
||
|
|
||
|
Here we know that there's only one `"Deadpond"`, and there shouldn't be any more than one.
|
||
|
|
||
|
If we run it once will output:
|
||
|
|
||
|
<div class="termy">
|
||
|
|
||
|
```console
|
||
|
$ python app.py
|
||
|
|
||
|
// Some boilerplate output omitted 😉
|
||
|
|
||
|
// The SELECT with WHERE
|
||
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
||
|
FROM hero
|
||
|
WHERE hero.name = ?
|
||
|
INFO Engine [no key 0.00015s] ('Deadpond',)
|
||
|
|
||
|
// Only one row found, we're good ✅
|
||
|
Hero: secret_name='Dive Wilson' age=None id=1 name='Deadpond'
|
||
|
```
|
||
|
|
||
|
</div>
|
||
|
|
||
|
But if we run it again, as it will create and insert all the heroes in the database again, they will be duplicated, and there will be more than one `"Deadpond"`. 😱
|
||
|
|
||
|
So, running it again, without first deleting the file `database.db` will output:
|
||
|
|
||
|
<div class="termy">
|
||
|
|
||
|
```console
|
||
|
$ python app.py
|
||
|
|
||
|
// Some boilerplate output omitted 😉
|
||
|
|
||
|
// The SELECT with WHERE
|
||
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
||
|
FROM hero
|
||
|
WHERE hero.name = ?
|
||
|
INFO Engine [no key 0.00015s] ('Deadpond',)
|
||
|
|
||
|
// Oh, no, the database is in a broken state, with duplicates! 🚨
|
||
|
Traceback (most recent call last):
|
||
|
|
||
|
// Some details about the error omitted
|
||
|
|
||
|
sqlalchemy.exc.MultipleResultsFound: Multiple rows were found when exactly one was required
|
||
|
```
|
||
|
|
||
|
</div>
|
||
|
|
||
|
## Exactly One with More Data
|
||
|
|
||
|
Of course, even if we don't duplicate the data, we could get the same error if we send a query that finds more than one row and expect exactly one with `.one()`:
|
||
|
|
||
|
```Python hl_lines="5 7"
|
||
|
# Code above omitted 👆
|
||
|
|
||
|
{!./docs_src/tutorial/one/tutorial004.py[ln:44-49]!}
|
||
|
|
||
|
# Code below omitted 👇
|
||
|
```
|
||
|
|
||
|
<details>
|
||
|
<summary>👀 Full file preview</summary>
|
||
|
|
||
|
```Python
|
||
|
{!./docs_src/tutorial/one/tutorial004.py!}
|
||
|
```
|
||
|
|
||
|
</details>
|
||
|
|
||
|
That would find 2 rows, and would end up with the same error.
|
||
|
|
||
|
## Exactly One with No Data
|
||
|
|
||
|
And also, if we get no rows at all with `.one()`, it will also raise an error:
|
||
|
|
||
|
```Python hl_lines="5 7"
|
||
|
# Code above omitted 👆
|
||
|
|
||
|
{!./docs_src/tutorial/one/tutorial005.py[ln:44-49]!}
|
||
|
|
||
|
# Code below omitted 👇
|
||
|
```
|
||
|
|
||
|
<details>
|
||
|
<summary>👀 Full file preview</summary>
|
||
|
|
||
|
```Python
|
||
|
{!./docs_src/tutorial/one/tutorial005.py!}
|
||
|
```
|
||
|
|
||
|
</details>
|
||
|
|
||
|
In this case, as there are no heroes with an age less than 25, `.one()` will raise an error.
|
||
|
|
||
|
This is what we would get if we run it in the command line:
|
||
|
|
||
|
<div class="termy">
|
||
|
|
||
|
```console
|
||
|
$ python app.py
|
||
|
|
||
|
// Some boilerplate output omitted 😉
|
||
|
|
||
|
// SELECT with WHERE
|
||
|
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
|
||
|
FROM hero
|
||
|
WHERE hero.age < ?
|
||
|
INFO Engine [no key 0.00014s] (25,)
|
||
|
|
||
|
// Oh, no, we expected one row but there aren't any! 🚨
|
||
|
Traceback (most recent call last):
|
||
|
|
||
|
// Some details about the error omitted
|
||
|
|
||
|
sqlalchemy.exc.NoResultFound: No row was found when one was required
|
||
|
```
|
||
|
|
||
|
</div>
|
||
|
|
||
|
## Compact Version
|
||
|
|
||
|
Of course, with `.first()` and `.one()` you would also probably write all that in a more compact form most of the time, all in a single line (or at least a single Python statement):
|
||
|
|
||
|
```Python hl_lines="5"
|
||
|
# Code above omitted 👆
|
||
|
|
||
|
{!./docs_src/tutorial/one/tutorial006.py[ln:44-47]!}
|
||
|
|
||
|
# Code below omitted 👇
|
||
|
```
|
||
|
|
||
|
<details>
|
||
|
<summary>👀 Full file preview</summary>
|
||
|
|
||
|
```Python
|
||
|
{!./docs_src/tutorial/one/tutorial006.py!}
|
||
|
```
|
||
|
|
||
|
</details>
|
||
|
|
||
|
That would result in the same as some examples above.
|
||
|
|
||
|
## Select by Id with `.where()`
|
||
|
|
||
|
In many cases you might want to select a single row by its Id column with the **primary key**.
|
||
|
|
||
|
You could do it the same way we have been doing with a `.where()` and then getting the first item with `.first()`:
|
||
|
|
||
|
```Python hl_lines="5 7"
|
||
|
# Code above omitted 👆
|
||
|
|
||
|
{!./docs_src/tutorial/one/tutorial007.py[ln:44-49]!}
|
||
|
|
||
|
# Code below omitted 👇
|
||
|
```
|
||
|
|
||
|
<details>
|
||
|
<summary>👀 Full file preview</summary>
|
||
|
|
||
|
```Python
|
||
|
{!./docs_src/tutorial/one/tutorial007.py!}
|
||
|
```
|
||
|
|
||
|
</details>
|
||
|
|
||
|
That would work correctly, as expected. But there's a shorter version. 👇
|
||
|
|
||
|
## Select by Id with `.get()`
|
||
|
|
||
|
As selecting a single row by its Id column with the **primary key** is a common operation, there's a shortcut for it:
|
||
|
|
||
|
```Python hl_lines="5"
|
||
|
# Code above omitted 👆
|
||
|
|
||
|
{!./docs_src/tutorial/one/tutorial008.py[ln:44-47]!}
|
||
|
|
||
|
# Code below omitted 👇
|
||
|
```
|
||
|
|
||
|
<details>
|
||
|
<summary>👀 Full file preview</summary>
|
||
|
|
||
|
```Python
|
||
|
{!./docs_src/tutorial/one/tutorial008.py!}
|
||
|
```
|
||
|
|
||
|
</details>
|
||
|
|
||
|
`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()`.
|
||
|
|
||
|
If you run it, it will output:
|
||
|
|
||
|
<div class="termy">
|
||
|
|
||
|
```console
|
||
|
$ python app.py
|
||
|
|
||
|
// Some boilerplate output omitted 😉
|
||
|
|
||
|
// SELECT with WHERE
|
||
|
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
|
||
|
WHERE hero.id = ?
|
||
|
INFO Engine [generated in 0.00021s] (1,)
|
||
|
|
||
|
// The printed result
|
||
|
Hero: secret_name='Dive Wilson' age=None id=1 name='Deadpond'
|
||
|
```
|
||
|
|
||
|
</div>
|
||
|
|
||
|
## 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):
|
||
|
|
||
|
```Python hl_lines="5"
|
||
|
# Code above omitted 👆
|
||
|
|
||
|
{!./docs_src/tutorial/one/tutorial009.py[ln:44-47]!}
|
||
|
|
||
|
# Code below omitted 👇
|
||
|
```
|
||
|
|
||
|
<details>
|
||
|
<summary>👀 Full file preview</summary>
|
||
|
|
||
|
```Python
|
||
|
{!./docs_src/tutorial/one/tutorial009.py!}
|
||
|
```
|
||
|
|
||
|
</details>
|
||
|
|
||
|
Running that will output:
|
||
|
|
||
|
<div class="termy">
|
||
|
|
||
|
```console
|
||
|
$ python app.py
|
||
|
|
||
|
// Some boilerplate output omitted 😉
|
||
|
|
||
|
// SELECT with WHERE
|
||
|
INFO Engine BEGIN (implicit)
|
||
|
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
|
||
|
WHERE hero.id = ?
|
||
|
INFO Engine [generated in 0.00024s] (9001,)
|
||
|
|
||
|
// No data found, so the value is None
|
||
|
Hero: None
|
||
|
```
|
||
|
|
||
|
</div>
|
||
|
|
||
|
## Recap
|
||
|
|
||
|
As querying the SQL database for a single row is a common operation, you know have several tools to do it in a short and simple way. 🎉
|