6

我有两个模型 - 照片和标签 - 通过 ManyToManyField 连接。

class Photo(models.Model):
    tags = models.ManyToManyField(Tag)

class Tag(models.Model):
    lang = models.CharField(max_length=2)
    name_es = models.CharField(max_length=40)
    name_en = models.CharField(max_length=40)

每隔一段时间,我们就会得到孤立的标签,任何照片都不再引用这些标签。有没有删除这些标签的有效方法?我知道这个答案: Django: delete M2M orphan entries?

我们的解决方案目前看起来是这样的:

for tag in Tag.objects.all():
    if not tag.photo_set.select_related(): tag.delete()

但是,随着数据库的增加,这个脚本的运行时间变得非常高:-P 有没有一种有效的方法可以从标签表中获取所有标签 ID 的列表,然后从多对多中获取所有标签 ID 的列表表来创建交集列表?

4

3 回答 3

4

尝试带有中间表的子查询

qs = Tag.objects.exclude(pk__in=Book.tags.through.objects.values('tag'))

# then you could
qs.delete()

# or if you need to trigger signal per item
for x in qs:
    x.delete()
于 2012-05-16T02:45:07.800 回答
2

我们不得不进一步提高这项任务的性能,所以我稍微修改了 okm 的解决方案:

    all_tag_pks = Tag.objects.values_list('pk', flat=True)
    used_tag_pks = Photo.tags.through.objects.values_list('tag', flat=True)
    Tag.objects.filter(pk__in=list(set(all_tag_pks) - set(used_tag_pks))).delete()

这样,对数据库的查询变得更小更快。

于 2012-11-12T23:58:19.640 回答
1

我找到了一种“实时”执行此操作的方法:

from django.db.models.signals import m2m_changed
from django.dispatch import receiver

class Photo(models.Model):
    tags = models.ManyToManyField(Tag)

class Tag(models.Model):
    lang = models.CharField(max_length=2)

@receiver(m2m_changed, sender=Photo.tags.through)
def delete_orphean_dateranges(sender, **kwargs):
    # when something is removed from the m2m:
    if kwargs['action'] == 'post_remove':  
        Tag.objects.filter(pk__in=kwargs['pk_set'], photo_set=None).delete()
        # select removed tags and check if they are not linked
        # to any Photo, and delete it

这样,每次从 m2m 中删除标签时编辑 Photo 的 m2m 时,都会调用此函数。

于 2018-11-12T18:05:18.237 回答