Skip to content

Commit d9c9501

Browse files
committed
try to improve the model deletion docs
1 parent 66b6353 commit d9c9501

File tree

1 file changed

+51
-15
lines changed
  • develop-docs/api-server/application-domains/database-migrations

1 file changed

+51
-15
lines changed

develop-docs/api-server/application-domains/database-migrations/index.mdx

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ Extra care is needed here if the table is referenced as a foreign key in other t
252252
- Remove any database level foreign key constraints from this table to other tables by setting `db_constraint=False` on the columns.
253253
- Remove the model and in the generated migration use `SafeDeleteModel(..., deletion_action=DeletionAction.MOVE_TO_PENDING)` to replace `DeleteModel(...)`. This only marks the state for the model as removed.
254254
- Deploy. It's important that all previous pull requests are in production before we remove the actual table.
255-
- Make a pull request that create a new migration that has the same `SafeDeleteModel` operation as before, but set `deletion_action=DeletionAction.DELETE` instead. This deletes the actual table from Postgres.
255+
- Make a pull request that creates a new migration that has the same `SafeDeleteModel` operation as before, but set `deletion_action=DeletionAction.DELETE` instead. This deletes the actual table from Postgres.
256256
- Deploy
257257

258258
Here's an example of removing this model:
@@ -272,14 +272,56 @@ class TestModel(Model):
272272

273273
First, we remove all references to this model from the codebase, including making sure that it's not referenced by any other models. This is best done as a separate pr to keep things clean.
274274

275-
Next we need to remove any db level foreign key constraints. To do this, we change this column and generate a migration:
275+
Next we produce two migrations, in individual prs that we will deploy separately.
276276

277+
First PR
277278
```python
278-
project = FlexibleForeignKey("sentry.Project", db_constraint=False)
279+
# <Model removed from code completely>
280+
281+
# First migration
282+
# ... Migration boilerplate ...
283+
operations = [
284+
migrations.AlterField(
285+
model_name="testmodel",
286+
name="project",
287+
field=sentry.db.models.fields.foreignkey.FlexibleForeignKey(
288+
db_constraint=False,
289+
on_delete=django.db.models.deletion.CASCADE,
290+
to="sentry.project",
291+
),
292+
),
293+
SafeDeleteModel(name="TestModel", deletion_action=DeletionAction.MOVE_TO_PENDING),
294+
]
295+
# ... Migration boilerplate ...
296+
```
297+
298+
Second PR:
299+
```python
300+
# Second migration
301+
# ... Migration boilerplate ...
302+
operations = [
303+
SafeDeleteModel(name="TestModel", deletion_action=DeletionAction.DELETE),
304+
]
305+
# ... Migration boilerplate ...
279306
```
280307

281-
The operations in the migration look like
308+
So once we have these two prs, we merge/deploy the first, and then the second and then the table is fully removed.
309+
310+
So to recap the steps here:
311+
- Remove all references to the model in the code in a separate pull request and merge. Doesn't matter if this deploys before the next step or not.
312+
- Remove any foreign key constraints and delete the model using `SafeDeleteModel(..., deletion_action=DeletionAction.MOVE_TO_PENDING)`. These operations can be in the same migration to save time.
313+
- Deploy all previous before continuing.
314+
- Remove the table from Postgres using `SafeDeleteModel(..., deletion_action=DeletionAction.DELETE),`
315+
316+
If you're comfortable producing these prs and deploying them, then stop here. Otherwise, this next section covers how to produce them in more detail.
317+
318+
To produce the first migration, we need to remove any db level foreign key constraints and remove the table from the codebase. To remove the db level foreign key constraints we add `db_constraint` to this column and generate a migration:
282319

320+
```python
321+
project = FlexibleForeignKey("sentry.Project", db_constraint=False)
322+
```
323+
324+
This produces a migration with operations like:
283325
```python
284326
operations = [
285327
migrations.AlterField(
@@ -294,23 +336,23 @@ operations = [
294336
]
295337
```
296338

297-
Once we've done this, we can now remove the model from code and generate the migration to remove it. The generated migration looks like this:
339+
Next, we remove the model from code and generate the migration to remove it. The generated migration looks like this:
298340

299341
```python
300342
operations = [
301343
migrations.DeleteModel(name="TestModel"),
302344
]
303345
```
304346

305-
Deleting this way is unsafe, so we want to replace this with `SafeDeleteModel` instead. So this becomes
347+
Django doesn't know about the `SafeDeleteModel` operation, so we replace it with that instead. This allows us to remove all state related to the model, but defer deleting it until a later migration. So this becomes
306348

307349
```python
308350
operations = [
309351
SafeDeleteModel(name="TestModel", deletion_action=DeletionAction.MOVE_TO_PENDING),
310352
]
311353
```
312354

313-
Using `SafeDeleteModel` allows us to remove all the state related to the model, but defer deleting it until a later migration. So now, we can combine the migration to remove the constraints and delete the model state together like so
355+
So now as a final step, we can combine these operations into a single migration, which is the first migration we want to deploy.
314356
```python
315357
operations = [
316358
migrations.AlterField(
@@ -326,21 +368,15 @@ operations = [
326368
]
327369
```
328370

329-
So now we deploy this migration and move onto the final step.
330-
331-
In this last step, we will use `SafeDeleteModel` again to finally remove the table from Postgres. First, we generate an empty migration using `sentry django makemigrations <your_app> --empty` to produce an empty migration, and then modify the operations to be like:
371+
To produce the second migration we generate an empty migration (`sentry django makemigrations <your_app> --empty`), then use the same `SafeDeleteModel` command from the previous migration, but change the deletion_action to `DeletionAction.DELETE`. This operation will remove the table from Postgres:
332372

333373
```python
334374
operations = [
335375
SafeDeleteModel(name="TestModel", deletion_action=DeletionAction.DELETE),
336376
]
337377
```
338378

339-
Then we deploy this and we're done. So to recap the steps here:
340-
- Remove all references to the model in the code in a separate pull request and merge. Doesn't matter if this deploys before the next step or not.
341-
- Remove any foreign key constraints and delete the model using `SafeDeleteModel(..., deletion_action=DeletionAction.MOVE_TO_PENDING)`. These operations can be in the same migration to save time.
342-
- Deploy all previous before continuing.
343-
- Remove the table from Postgres using `SafeDeleteModel(..., deletion_action=DeletionAction.DELETE),`
379+
This second PR will contain only the migration and related boilerplate.
344380

345381
### Foreign Keys
346382

0 commit comments

Comments
 (0)