【问题标题】:Efficiently delete orphaned m2m objects/tags in Django在 Django 中有效地删除孤立的 m2m 对象/标签
【发布时间】:2012-05-15 22:29:40
【问题描述】:

我有两个模型 - 照片和标签 - 通过 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 的列表- to-many 表来创建交集列表?

【问题讨论】:

    标签: django many-to-many orphan orphaned-objects


    【解决方案1】:

    尝试使用中间表进行子查询

    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()
    

    【讨论】:

      【解决方案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()
      

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

      【讨论】:

        【解决方案3】:

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

        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, 这个函数被调用了。

        【讨论】:

        • 感谢您的建议。我没试过。但这只有在编辑很少发生的情况下才有效,否则会对性能造成巨大影响(就像我们的例子一样)。
        • 嗯 当然在m2m上的case删除操作中会发出一个sql查询,但是查询对我来说似乎不是特别复杂。它仅在删除编辑时触发,因此显然它较慢,但我不会称其为巨大的,这取决于您对数据库的期望。
        • 这仅适用于我们的特定用例。我们每秒有数百次这样的编辑......但这是保持系统简单和自我管理的好方法。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-04
        • 2016-02-18
        • 1970-01-01
        • 2013-11-25
        • 2020-05-03
        相关资源
        最近更新 更多