Convert verification_model to ForeignKey with data migration

- Convert CodeBatch.verification_model from TextField to ForeignKey(VerificationModel)
- Add ForeignKey relationship to CodeBatchResource API
- Update QrVerifyView to use verification_model.name instead of string
- Create data migration to:
  - Rename old TextField temporarily
  - Add new ForeignKey field
  - Migrate existing model names to VerificationModel entries
  - Link batches to VerificationModel records
  - Remove old TextField
- GenericManager will now show dropdown/selector UI automatically
This commit is contained in:
Fam Zheng 2025-11-25 22:38:57 +00:00
parent 2c74d59d37
commit 719ccd6477
3 changed files with 85 additions and 3 deletions

View File

@ -0,0 +1,80 @@
# Generated by Django 4.2.23 on 2025-11-25 22:38
from django.db import migrations, models
import django.db.models.deletion
def migrate_verification_model_data(apps, schema_editor):
"""Migrate data from TextField to ForeignKey"""
CodeBatch = apps.get_model('products', 'CodeBatch')
VerificationModel = apps.get_model('products', 'VerificationModel')
# Query the database directly to get the old TextField values
from django.db import connection
with connection.cursor() as cursor:
# Query the old field (verification_model_old after rename)
cursor.execute("SELECT DISTINCT verification_model_old FROM products_codebatch WHERE verification_model_old IS NOT NULL AND verification_model_old != ''")
rows = cursor.fetchall()
unique_model_names = {row[0] for row in rows if row[0]}
# Create VerificationModel entries for each unique model name
model_map = {}
for model_name in unique_model_names:
vm, created = VerificationModel.objects.get_or_create(name=model_name)
model_map[model_name] = vm
# Update all CodeBatch records to use the ForeignKey
with connection.cursor() as cursor:
cursor.execute("SELECT id, verification_model_old FROM products_codebatch WHERE verification_model_old IS NOT NULL AND verification_model_old != ''")
rows = cursor.fetchall()
for batch_id, model_name in rows:
if model_name in model_map:
vm = model_map[model_name]
CodeBatch.objects.filter(pk=batch_id).update(verification_model=vm)
def reverse_migrate_verification_model_data(apps, schema_editor):
"""Reverse migration: convert ForeignKey back to TextField"""
CodeBatch = apps.get_model('products', 'CodeBatch')
# Extract the name from the ForeignKey and set it in the old field
from django.db import connection
with connection.cursor() as cursor:
cursor.execute("""
SELECT cb.id, vm.name
FROM products_codebatch cb
LEFT JOIN products_verificationmodel vm ON cb.verification_model_id = vm.id
WHERE cb.verification_model_id IS NOT NULL
""")
rows = cursor.fetchall()
for batch_id, model_name in rows:
if model_name:
CodeBatch.objects.filter(pk=batch_id).update(verification_model_old=model_name)
class Migration(migrations.Migration):
dependencies = [
('products', '0110_add_verification_model'),
]
operations = [
# Step 1: Rename the old TextField to a temporary name
migrations.RenameField(
model_name='codebatch',
old_name='verification_model',
new_name='verification_model_old',
),
# Step 2: Add the new ForeignKey field
migrations.AddField(
model_name='codebatch',
name='verification_model',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='batches', to='products.verificationmodel', verbose_name='验证模型(可选,留空使用默认模型)'),
),
# Step 3: Migrate the data
migrations.RunPython(migrate_verification_model_data, reverse_migrate_verification_model_data),
# Step 4: Remove the old TextField
migrations.RemoveField(
model_name='codebatch',
name='verification_model_old',
),
]

View File

@ -194,8 +194,9 @@ class CodeBatch(models.Model):
verbose_name="小程序入口路径(可选)")
mini_prog_entry_env_version = models.CharField(max_length=128, null=True, blank=True, default='release',
verbose_name="小程序入口环境版本(可选), 默认release。如为trial则进入体验版")
verification_model = models.TextField(null=True, blank=True,
verbose_name="验证模型名称(可选,留空使用默认模型)")
verification_model = models.ForeignKey('VerificationModel', related_name='batches', null=True, blank=True,
on_delete=models.SET_NULL,
verbose_name="验证模型(可选,留空使用默认模型)")
def gen_code(self, n):
a = 10 ** (self.num_digits - 1)

View File

@ -326,6 +326,7 @@ class ProductPropertyResource(BaseResource):
class CodeBatchResource(BaseResource):
tenant = ForeignKey(TenantResource, 'tenant', null=True, blank=True)
verification_model = ForeignKey(VerificationModelResource, 'verification_model', null=True, blank=True)
class Meta:
queryset = CodeBatch.objects.all().order_by('-pk')
resource_name = 'code-batch'
@ -739,7 +740,7 @@ class QrVerifyView(BaseView):
t = threading.Thread(target=oss_put, args=(image_name, img_data))
t.run()
if sc.batch.feature_comparison_threshold > 0.01:
model_name = sc.batch.verification_model if sc.batch.verification_model else None
model_name = sc.batch.verification_model.name if sc.batch.verification_model else None
used_model = self.do_v5_qr_verify(imgs, messages, model_name=model_name)
sd.verification_model = used_model if used_model else None
else: