mirror of
https://github.com/zulip/zulip.git
synced 2025-11-05 06:23:38 +00:00
A user ran into an issue while upgrading where ContentType.objects.get(model="realmreactivationstatus", app_label="zerver") fails due to the object being missing. The reason for that is to be yet figured out, but the immediate solution is clear in the sense that the migration can just quit early if not Confirmation.objects.filter(type=REALM_REACTIVATION).exists() and that'll effectively skip it for almost all servers (because realm reactivations links are something that's really only useful on Zulip Cloud).
80 lines
3.4 KiB
Python
80 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 = ContentType.objects.get(
|
|
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),
|
|
]
|