feat(problem): 标签名去空格并加大小写不敏感唯一约束
迁移时先合并已存在的重复标签,题目关系转移到保留的那个。 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
49
problem/migrations/0014_problem_tag_ci_unique.py
Normal file
49
problem/migrations/0014_problem_tag_ci_unique.py
Normal file
@@ -0,0 +1,49 @@
|
||||
from collections import defaultdict
|
||||
|
||||
from django.db import migrations, models
|
||||
from django.db.models.functions import Lower
|
||||
|
||||
|
||||
def merge_duplicate_tags(apps, schema_editor):
|
||||
"""加唯一约束前,把「去空格+转小写」后同名的标签合并成一个。
|
||||
|
||||
每组保留关联题目数最多的(并列时保留 id 最小的)作为主标签,
|
||||
其余标签下的题目关系转移到主标签后删除。空名标签同样按此规则归并成一条,
|
||||
不做删除,留给管理员在标签管理页处理。
|
||||
"""
|
||||
ProblemTag = apps.get_model("problem", "ProblemTag")
|
||||
Problem = apps.get_model("problem", "Problem")
|
||||
|
||||
groups = defaultdict(list)
|
||||
for tag in ProblemTag.objects.all():
|
||||
groups[(tag.name or "").strip().lower()].append(tag)
|
||||
|
||||
for tags in groups.values():
|
||||
counts = {tag.id: Problem.objects.filter(tags=tag).count() for tag in tags}
|
||||
tags.sort(key=lambda tag: (-counts[tag.id], tag.id))
|
||||
primary = tags[0]
|
||||
|
||||
stripped = (primary.name or "").strip()
|
||||
if primary.name != stripped:
|
||||
primary.name = stripped
|
||||
primary.save(update_fields=["name"])
|
||||
|
||||
for duplicate in tags[1:]:
|
||||
for problem in Problem.objects.filter(tags=duplicate):
|
||||
problem.tags.add(primary)
|
||||
problem.tags.remove(duplicate)
|
||||
duplicate.delete()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("problem", "0013_remove_problem_io_mode_remove_problem_rule_type_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(merge_duplicate_tags, migrations.RunPython.noop),
|
||||
migrations.AddConstraint(
|
||||
model_name="problemtag",
|
||||
constraint=models.UniqueConstraint(Lower("name"), name="problem_tag_name_ci_unique"),
|
||||
),
|
||||
]
|
||||
@@ -1,4 +1,5 @@
|
||||
from django.db import models
|
||||
from django.db.models.functions import Lower
|
||||
|
||||
from account.models import User
|
||||
from contest.models import Contest
|
||||
@@ -11,6 +12,13 @@ class ProblemTag(models.Model):
|
||||
|
||||
class Meta:
|
||||
db_table = "problem_tag"
|
||||
constraints = [
|
||||
models.UniqueConstraint(Lower("name"), name="problem_tag_name_ci_unique"),
|
||||
]
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.name = (self.name or "").strip()
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
|
||||
class Problem(models.Model):
|
||||
|
||||
Reference in New Issue
Block a user