Files
zulip/zerver/migrations/0401_migrate_old_realm_reactivation_links.py
Anders Kaseorg df001db1a9 black: Reformat with Black 23.
Black 23 enforces some slightly more specific rules about empty line
counts and redundant parenthesis removal, but the result is still
compatible with Black 22.

(This does not actually upgrade our Python environment to Black 23
yet.)

Signed-off-by: Anders Kaseorg <anders@zulip.com>
2023-02-02 10:40:13 -08:00

79 lines
3.4 KiB
Python

from django.db import migrations
from django.db.backends.postgresql.schema import BaseDatabaseSchemaEditor
from django.db.migrations.state import StateApps
def fix_old_realm_reactivation_confirmations(
apps: StateApps, schema_editor: BaseDatabaseSchemaEditor
) -> None:
"""
Migration 0400_realmreactivationstatus changed REALM_REACTIVATION Confirmation
to have a RealmReactivationStatus instance as .content_object. Now we need to migrate
pre-existing REALM_REACTIVATION Confirmations to follow this format.
The process is a bit fiddly because Confirmation.content_object is a GenericForeignKey,
which can't be directly accessed in migration code, so changing it involves manually
updating the .object_id and .content_type attributes underpinning it.
For these old Confirmation we don't have a mechanism for tracking which have been used,
so it's safest to just revoke them all. If any users need a realm reactivation link, it
can just be re-generated.
"""
REALM_REACTIVATION = 8
RealmReactivationStatus = apps.get_model("zerver", "RealmReactivationStatus")
Realm = apps.get_model("zerver", "Realm")
Confirmation = apps.get_model("confirmation", "Confirmation")
ContentType = apps.get_model("contenttypes", "ContentType")
if not Confirmation.objects.filter(type=REALM_REACTIVATION).exists():
# No relevant Confirmations so nothing to do, and the database may actually
# no be provisioned yet, which would make the code below break.
return
# .content_type of these old Confirmation will be changed to this.
realm_reactivation_status_content_type, created = ContentType.objects.get_or_create(
model="realmreactivationstatus", app_label="zerver"
)
for confirmation in Confirmation.objects.filter(type=REALM_REACTIVATION):
if confirmation.content_type_id == realm_reactivation_status_content_type.id:
# This Confirmation is already in the new format.
continue
assert confirmation.content_type.model == "realm"
realm_object_id = confirmation.object_id
# Sanity check that the realm exists.
try:
Realm.objects.get(id=realm_object_id)
except Realm.DoesNotExist:
print(
f"Confirmation {confirmation.id} is tied to realm_id {realm_object_id} which doesn't exist. "
"This is unexpected! Skipping migrating it."
)
continue
# We create the object with STATUS_REVOKED.
new_content_object = RealmReactivationStatus(realm_id=realm_object_id, status=2)
new_content_object.save()
# Now we can finally change the .content_object. This is done by setting
# .content_type to the correct ContentType as mentioned above and the object_id
# to the id of the RealmReactivationStatus instance that's supposed to be
# the content_object. This works because .content_object is dynamically
# derived by django from the .content_type and object_id values.
confirmation.content_type_id = realm_reactivation_status_content_type
confirmation.object_id = new_content_object.id
confirmation.save()
class Migration(migrations.Migration):
dependencies = [
("zerver", "0400_realmreactivationstatus"),
]
operations = [
migrations.RunPython(fix_old_realm_reactivation_confirmations, elidable=True),
]