File: delete.md

package info (click to toggle)
sqlmodel 0.0.25-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 17,456 kB
  • sloc: python: 34,346; javascript: 280; sh: 15; makefile: 7
file content (248 lines) | stat: -rw-r--r-- 6,827 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# Delete Data - DELETE

Now let's delete some data using **SQLModel**.

## Continue From Previous Code

As before, we'll continue from where we left off with the previous code.

{* ./docs_src/tutorial/update/tutorial003_py310.py ln[0] *}

Remember to remove the `database.db` file before running the examples to get the same results.

## Delete with SQL

This `Spider-Youngster` is getting too weird, so let's just delete it.

But don't worry, we'll reboot it later with a new story. 😅

Let's see how to delete it with **SQL**:

```SQL hl_lines="1"
DELETE
FROM hero
WHERE name = "Spider-Youngster"
```

This means, more or less:

> Hey SQL database 👋, I want to `DELETE` rows `FROM` the table called `hero`.
>
> Please delete all the rows `WHERE` the value of the column `name` is equal to `"Spider-Youngster"`.

Remember that when using a `SELECT` statement it has the form:

```SQL
SELECT [some stuff here]
FROM [name of a table here]
WHERE [some condition here]
```

`DELETE` is very similar, and again we use `FROM` to tell the table to work on, and we use `WHERE` to tell the condition to use to match the rows that we want to delete.

You can try that in **DB Browser for SQLite**:

<img class="shadow" src="/img/tutorial/delete/image01.png">

Have 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).

Now let's delete with **SQLModel**.

To get the same results, delete the `database.db` file before running the examples.

## Read From the Database

We'll start by selecting the hero `"Spider-Youngster"` that we updated in the previous chapter, this is the one we will delete:

{* ./docs_src/tutorial/delete/tutorial001_py310.py ln[70:75] hl[72] *}

As this is a new function `delete_heroes()`, we'll also add it to the `main()` function so that we call it when executing the program from the command line:

{* ./docs_src/tutorial/delete/tutorial001_py310.py ln[90:98] hl[94] *}

That will print the same existing hero **Spider-Youngster**:

<div class="termy">

```console
$ python app.py

// Some boilerplate and previous output omitted 😉

// The SELECT with WHERE
INFO Engine BEGIN (implicit)
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
WHERE hero.name = ?
INFO Engine [no key 0.00011s] ('Spider-Youngster',)

// Print the hero as obtained from the database
Hero:  name='Spider-Youngster' secret_name='Pedro Parqueador' age=16 id=2
```

</div>

## Delete the Hero from the Session

Now, very similar to how we used `session.add()` to add or update new heroes, we can use `session.delete()` to delete the hero from the session:

{* ./docs_src/tutorial/delete/tutorial001_py310.py ln[70:77] hl[77] *}

## Commit the Session

To save the current changes in the session, **commit** it.

This will save all the changes stored in the **session**, like the deleted hero:

{* ./docs_src/tutorial/delete/tutorial001_py310.py ln[70:78] hl[78] *}

The same as we have seen before, `.commit()` will also save anything else that was added to the session. Including updates, or created heroes.

This commit after deleting the hero will generate this output:

<div class="termy">

```console
$ python app.py

// Some boilerplate output omitted 😉

// Previous output omitted 🙈

// The SQL to update the hero in the database
INFO Engine DELETE FROM hero WHERE hero.id = ?
INFO Engine [generated in 0.00020s] (2,)
INFO Engine COMMIT
```

</div>

## Print the Deleted Object

Now the hero is deleted from the database.

If we tried to use `session.refresh()` with it, it would raise an exception, because there's no data in the database for this hero.

Nevertheless, the object is still available with its data, but now it's not connected to the session and it no longer exists in the database.

As the object is not connected to the session, it is not marked as "expired", the session doesn't even care much about this object anymore.

Because of that, the object still contains its attributes with the data in it, so we can print it:

{* ./docs_src/tutorial/delete/tutorial001_py310.py ln[70:80] hl[80] *}

This will output:

<div class="termy">

```console
$ python app.py

// Some boilerplate output omitted 😉

// Previous output omitted 🙈

// Print the deleted hero
Deleted hero: name='Spider-Youngster' secret_name='Pedro Parqueador' age=16 id=2
```

</div>

## Query the Database for the Same Row

To confirm if it was deleted, now let's query the database again, with the same `"Spider-Youngster"` name:

{* ./docs_src/tutorial/delete/tutorial001_py310.py ln[70:84] hl[82:84] *}

Here we are using `results.first()` to get the first object found (in case it found multiple) or `None`, if it didn't find anything.

If we used `results.one()` instead, it would raise an exception, because it expects exactly one result.

And because we just deleted that hero, this should not find anything and we should get `None`.

This will execute some SQL in the database and output:

<div class="termy">

```console
$ python app.py

// Some boilerplate output omitted 😉

// Previous output omitted 🙈

// Automatically start a new transaction
INFO Engine BEGIN (implicit)

// SQL to search for the hero
INFO Engine SELECT hero.id, hero.name, hero.secret_name, hero.age
FROM hero
WHERE hero.name = ?
INFO Engine [no key 0.00013s] ('Spider-Youngster',)
```

</div>

## Confirm the Deletion

Now let's just confirm that, indeed, no hero was found in the database with that name.

We'll do it by checking that the "first" item in the `results` is `None`:

{* ./docs_src/tutorial/delete/tutorial001_py310.py ln[70:87] hl[86:87] *}

This will output:

<div class="termy">

```console
$ python app.py

// Some boilerplate output omitted 😉

// Previous output omitted 🙈

// Indeed, the hero was deleted 🔥
There's no hero named Spider-Youngster

// Cleanup after the with block
INFO Engine ROLLBACK
```

</div>

## Review the Code

Now let's review all that code:

//// tab | Python 3.10+

```{ .python .annotate hl_lines="70-88" }
{!./docs_src/tutorial/delete/tutorial002_py310.py!}
```

{!./docs_src/tutorial/delete/annotations/en/tutorial002.md!}

////

//// tab | Python 3.8+

```{ .python .annotate hl_lines="72-90" }
{!./docs_src/tutorial/delete/tutorial002.py!}
```

{!./docs_src/tutorial/delete/annotations/en/tutorial002.md!}

////

/// tip

Check out the number bubbles to see what is done by each line of code.

///

## Recap

To delete rows with **SQLModel** you just have to `.delete()` them with the **session**, and then, as always, `.commit()` the session to save the changes to the database. 🔥