mirror of
https://github.com/jlengrand/bugsink.git
synced 2026-03-10 08:01:17 +00:00
Like e45c61d6f0, but for .project.
I originally thought `SET_NULL` would be a good way to "do stuff later", but
that's only so the degree that [1] updates are cheaper than deletes and [2]
2nd-order effects (further deletes in the dep-tree) are avoided.
Now that we have explicit Project-deletion (deps-first, delayed, properly batched)
the SET_NULL behavior is always a no-op (but with cost in queries).
As a result, in the test for project deletion (which has deletes for many
of the altered models), the following 12 queries are no longer done:
```
SELECT "projects_project"."id", [..many fields..] FROM "projects_project" WHERE "projects_project"."id" = 1
DELETE FROM "projects_projectmembership" WHERE "projects_projectmembership"."project_id" IN (1)
DELETE FROM "alerts_messagingserviceconfig" WHERE "alerts_messagingserviceconfig"."project_id" IN (1)
UPDATE "releases_release" SET "project_id" = NULL WHERE "releases_release"."project_id" IN (1)
UPDATE "issues_issue" SET "project_id" = NULL WHERE "issues_issue"."project_id" IN (1)
UPDATE "issues_grouping" SET "project_id" = NULL WHERE "issues_grouping"."project_id" IN (1)
UPDATE "events_event" SET "project_id" = NULL WHERE "events_event"."project_id" IN (1)
UPDATE "tags_tagkey" SET "project_id" = NULL WHERE "tags_tagkey"."project_id" IN (1)
UPDATE "tags_tagvalue" SET "project_id" = NULL WHERE "tags_tagvalue"."project_id" IN (1)
UPDATE "tags_eventtag" SET "project_id" = NULL WHERE "tags_eventtag"."project_id" IN (1)
UPDATE "tags_issuetag" SET "project_id" = NULL WHERE "tags_issuetag"."project_id" IN (1)
```
49 lines
2.1 KiB
Python
49 lines
2.1 KiB
Python
from django.db import migrations
|
|
|
|
|
|
def delete_objects_pointing_to_null_project(apps, schema_editor):
|
|
# Up until now, we have various models w/ .project=FK(null=True, on_delete=models.SET_NULL)
|
|
# Although it is "not expected" in the interface, project-deletion would have led to those
|
|
# objects with a null project. We're about to change that to .project=FK(null=False, ...) which
|
|
# would crash if we don't remove those objects first. Object-removal is "fine" though, because
|
|
# as per the meaning of the SET_NULL, these objects were "dangling" anyway.
|
|
|
|
# We implement this as a _single_ cross-app migration so that reasoning about the order of deletions is easy (and
|
|
# we can just copy the correct order from the project/tasks.py `preferred` variable. This cross-appness does mean
|
|
# that we must specify all dependencies here, and all the set-null migrations (from various apps) must point at this
|
|
# migration as their dependency.
|
|
|
|
# from tasks.py, but in "strings" form
|
|
preferred = [
|
|
'tags.EventTag',
|
|
'tags.IssueTag',
|
|
'tags.TagValue',
|
|
'tags.TagKey',
|
|
# 'issues.TurningPoint', # not needed, .project is already not-null (we just added it)
|
|
'events.Event',
|
|
'issues.Grouping',
|
|
# 'alerts.MessagingServiceConfig', was CASCADE (not null), so no deletion needed
|
|
# 'projects.ProjectMembership', was CASCADE (not null), so no deletion needed
|
|
'releases.Release',
|
|
'issues.Issue',
|
|
]
|
|
|
|
for model_name in preferred:
|
|
model = apps.get_model(*model_name.split('.'))
|
|
model.objects.filter(project__isnull=True).delete()
|
|
|
|
|
|
class Migration(migrations.Migration):
|
|
|
|
dependencies = [
|
|
("projects", "0012_project_is_deleted"),
|
|
("issues", "0024_turningpoint_project_alter_not_null"),
|
|
("tags", "0004_alter_do_nothing"),
|
|
("releases", "0002_release_releases_re_sort_ep_5c07c8_idx"),
|
|
("events", "0021_alter_do_nothing"),
|
|
]
|
|
|
|
operations = [
|
|
migrations.RunPython(delete_objects_pointing_to_null_project, reverse_code=migrations.RunPython.noop),
|
|
]
|