from django.db import migrations, models def canonical_mobile(value): if value is None: return None text = str(value) if len(text) == 12 and text.startswith('98'): return int(text[2:]) return value def normalized_rows(model, alias, per_workspace=False): seen = {} updates = [] fields = ('pk', 'mobile', 'sys_ws_id') if per_workspace else ('pk', 'mobile') for row in model.objects.using(alias).only(*fields).iterator(): mobile = canonical_mobile(row.mobile) key = (row.sys_ws_id, mobile) if per_workspace else mobile if mobile is not None and key in seen: raise RuntimeError( f'Duplicate mobiles in {model._meta.label}: record IDs {seen[key]} and {row.pk}. ' 'Resolve these records before migration. No records were deleted.' ) if mobile is not None: seen[key] = row.pk if mobile != row.mobile: row.mobile = mobile updates.append(row) return updates def check_and_normalize_mobiles(apps, schema_editor): alias = schema_editor.connection.alias customer = apps.get_model('customer', 'Customer') user = apps.get_model('cuser', 'CUser') role = apps.get_model('cuser', 'CuserRole') # Audit both tables before writing. A collision never silently merges people. customer_updates = normalized_rows(customer, alias, per_workspace=True) user_updates = normalized_rows(user, alias) role_updates = [] for row in role.objects.using(alias).only('pk', 'mobile').iterator(): normalized = canonical_mobile(row.mobile) if normalized != row.mobile: row.mobile = normalized role_updates.append(row) customer.objects.using(alias).bulk_update(customer_updates, ['mobile'], batch_size=500) user.objects.using(alias).bulk_update(user_updates, ['mobile'], batch_size=500) role.objects.using(alias).bulk_update(role_updates, ['mobile'], batch_size=500) class Migration(migrations.Migration): dependencies = [('customer', '0007_alter_customer_name_family'), ('cuser', '0013_alter_cuser_name_family')] operations = [ migrations.RunPython(check_and_normalize_mobiles, migrations.RunPython.noop), migrations.AddConstraint( model_name='customer', constraint=models.UniqueConstraint(fields=('sys_ws', 'mobile'), name='customer_workspace_mobile_unique'), ), ]