migration repaire
This commit is contained in:
1 parent
c8d45dd07c
commit
56d8760613
11 files changed
+492
-5064
No files matched your search
@@ -1,4 +1,5 @@
|
||||
from django.db import migrations, models
|
||||
from apps.customer.mobile_migration_repair import repair_conflicts
|
||||
|
||||
|
||||
def canonical_mobile(value):
|
||||
@@ -10,11 +11,13 @@ def canonical_mobile(value):
|
||||
return value
|
||||
|
||||
|
||||
def normalized_rows(model, alias, per_workspace=False):
|
||||
def normalized_rows(model, alias, per_workspace=False, skip_ids=None):
|
||||
seen = {}
|
||||
updates = []
|
||||
fields = ('pk', 'mobile', 'sys_ws_id') if per_workspace else ('pk', 'mobile')
|
||||
for row in model.objects.using(alias).only(*fields).iterator():
|
||||
if skip_ids and row.pk in skip_ids:
|
||||
continue
|
||||
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:
|
||||
@@ -35,9 +38,10 @@ def check_and_normalize_mobiles(apps, schema_editor):
|
||||
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.
|
||||
# Quarantine legacy conflicts automatically, retaining a persistent audit.
|
||||
skipped_users = repair_conflicts(apps, schema_editor, canonical_mobile)
|
||||
customer_updates = normalized_rows(customer, alias, per_workspace=True)
|
||||
user_updates = normalized_rows(user, alias)
|
||||
user_updates = normalized_rows(user, alias, skip_ids=skipped_users)
|
||||
role_updates = []
|
||||
for row in role.objects.using(alias).only('pk', 'mobile').iterator():
|
||||
normalized = canonical_mobile(row.mobile)
|
||||
@@ -47,6 +51,11 @@ def check_and_normalize_mobiles(apps, schema_editor):
|
||||
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)
|
||||
if schema_editor.connection.vendor == 'postgresql':
|
||||
# Drain deferred FK checks from panel detachment before AddConstraint
|
||||
# alters the same table in this atomic migration.
|
||||
with schema_editor.connection.cursor() as cursor:
|
||||
cursor.execute('SET CONSTRAINTS ALL IMMEDIATE')
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
from django.db import migrations, models
|
||||
from django.utils import timezone
|
||||
|
||||
|
||||
class CreateAuditModel(migrations.CreateModel):
|
||||
"""0008 can create this audit table before repairing legacy data.
|
||||
|
||||
Existing installations that already applied 0008 create it here instead.
|
||||
Keep audit history on rollback; it is evidence of irreversible data repairs.
|
||||
"""
|
||||
def database_forwards(self, app_label, schema_editor, from_state, to_state):
|
||||
model = to_state.apps.get_model(app_label, self.name)
|
||||
if model._meta.db_table not in schema_editor.connection.introspection.table_names():
|
||||
super().database_forwards(app_label, schema_editor, from_state, to_state)
|
||||
|
||||
def database_backwards(self, app_label, schema_editor, from_state, to_state):
|
||||
pass
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [('customer', '0008_customer_mobile_unique')]
|
||||
operations = [CreateAuditModel(
|
||||
name='MobileConflict',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('source_model', models.CharField(max_length=100)),
|
||||
('record_id', models.PositiveBigIntegerField()),
|
||||
('workspace_id', models.PositiveBigIntegerField(null=True, blank=True)),
|
||||
('original_mobile', models.PositiveBigIntegerField(null=True, blank=True)),
|
||||
('canonical_mobile', models.PositiveBigIntegerField()),
|
||||
('snapshot', models.JSONField()),
|
||||
('created_at', models.DateTimeField(default=timezone.now, editable=False)),
|
||||
],
|
||||
options={'ordering': ['-created_at', '-pk'],
|
||||
'unique_together': (('source_model', 'record_id'),),
|
||||
'verbose_name': 'تعارض شماره قدیمی', 'verbose_name_plural': 'تعارضهای شماره قدیمی'},
|
||||
)]
|
||||
Reference in new issue
Block a user