# Generated by Django 5.2.3 on 2025-07-04 20:33 from datetime import timedelta from django.conf import settings from django.db import migrations, transaction from django.db.backends.base.schema import BaseDatabaseSchemaEditor from django.db.migrations.state import StateApps from django.db.models import Max def migrate_realmcreationkey_to_realmcreationstatus( apps: StateApps, schema_editor: BaseDatabaseSchemaEditor ) -> None: """ This migration is for switching from using a separate RealmCreationKey class for realm creation keys to just using the Confirmation system. The aim is to iterate through all the existing RealmCreationKey and create a corresponding Confirmation+RealmCreationStatus. For validity of these objects, we only need to worry about RealmCreationKeys expired due to time. This is taken care of by making sure we set expiry_date on the Confirmation we're creating. The way the RealmCreationKey system worked was to .delete() the RealmCreationKey objects when they were used - so those simply no longer exist and we don't need to worry about this case. """ CAN_CREATE_REALM = 11 Confirmation = apps.get_model("confirmation", "Confirmation") ContentType = apps.get_model("contenttypes", "ContentType") RealmCreationKey = apps.get_model("confirmation", "RealmCreationKey") RealmCreationStatus = apps.get_model("zerver", "RealmCreationStatus") realm_creation_status_content_type, _created = ContentType.objects.get_or_create( model="realmcreationstatus", app_label="zerver" ) BATCH_SIZE = 10000 max_id = RealmCreationKey.objects.aggregate(Max("id"))["id__max"] if max_id is None: # Nothing to do. return lower_bound = 1 while lower_bound <= max_id + (BATCH_SIZE / 2): upper_bound = lower_bound + BATCH_SIZE - 1 creation_keys = RealmCreationKey.objects.filter(id__range=(lower_bound, upper_bound)) creation_statuses_to_create = [] confirmations_to_create = [] for creation_key in creation_keys: # These keys were generated in the same way as keys for Confirmation objects, # so we can copy them over without breaking anything. key = creation_key.creation_key date_created = creation_key.date_created presume_email_valid = creation_key.presume_email_valid creation_status = RealmCreationStatus( status=0, date_created=date_created, presume_email_valid=presume_email_valid ) confirmation = Confirmation( content_type=realm_creation_status_content_type, type=CAN_CREATE_REALM, confirmation_key=key, date_sent=date_created, expiry_date=date_created + timedelta(days=settings.CAN_CREATE_REALM_LINK_VALIDITY_DAYS), ) # To attach the Confirmations to RealmCreationStatus objects we need to set the # confirmation.object_id to their respective ids. But we haven't saved # the RealmCreationStatus objs to the database yet - so we don't have ids. # # After we .bulk_create() them, their .id attributes will be populated. # So for now we just link the RealmCreationStatus to the Confirmation # via a temporary ._object attribute - which we'll use later to set # the .object_id as intended. confirmation._object = creation_status creation_statuses_to_create.append(creation_status) confirmations_to_create.append(confirmation) with transaction.atomic(): RealmCreationStatus.objects.bulk_create(creation_statuses_to_create) # Now the objects in creation_statuses_to_create have had their .id # attrs populated. For every confirmation, confirmation._object # points to its corresponding RealmCreationStatus - so now we can # set the confirmation.object_id values and clear out the temporary # ._object attr. for confirmation in confirmations_to_create: confirmation.object_id = confirmation._object.id delattr(confirmation, "_object") Confirmation.objects.bulk_create(confirmations_to_create) lower_bound += BATCH_SIZE class Migration(migrations.Migration): atomic = False dependencies = [ ("confirmation", "0015_alter_confirmation_object_id"), ("zerver", "0747_realmcreationstatus"), ] operations = [ migrations.RunPython( migrate_realmcreationkey_to_realmcreationstatus, reverse_code=migrations.RunPython.noop, elidable=True, ), ]