18

鉴于这两个模型:

class Item(models.Model):
    timestamp = models.DateTimeField()

class Source(models.Model):
    items = models.ManyToManyField(Item, related_name="sources")

我可以使用这个在给定时间之前找到所有源的项目:

source.items.filter(timestamp__lte=some_datetime)

如何有效地删除与该查询匹配的所有项目?我想我可以尝试这样的事情:

items_to_remove = list(source.items.filter(timestamp__lte=some_datetime))
source.items.remove(*items_to_remove)

但这似乎很糟糕。

请注意,我不想删除这些项目,因为它们也可能属于其他来源。我只是想删除他们与特定来源的关系。

4

2 回答 2

34

我认为您在金钱上做对了,除了您不需要转换为列表。

source.items.remove(*source.items.filter(*args))

/方法如下remove所示add

remove(self, *objs)
add(self, *objs)

并且文档使用添加多个示例的形式,[p1, p2, p3]所以我打赌同样适用remove,因为参数是相同的。

>>> a2.publications.add(p1, p2, p3)

再深入一点,remove 函数一个接一个地迭代*objs,检查它是否是有效模型,否则使用值作为 PK,然后删除带有 a 的项目pk__in,所以我会说是的,最好的方法是首先查询您的 m2m 表以查找要删除的对象,然后将这些对象传递到 m2m 管理器中。

    # django.db.models.related.py
    def _remove_items(self, source_field_name, target_field_name, *objs):
        # source_col_name: the PK colname in join_table for the source object
        # target_col_name: the PK colname in join_table for the target object
        # *objs - objects to remove

        # If there aren't any objects, there is nothing to do.
        if objs:
            # Check that all the objects are of the right type
            old_ids = set()
            for obj in objs:
                if isinstance(obj, self.model):
                    old_ids.add(obj.pk)
                else:
                    old_ids.add(obj)
            if self.reverse or source_field_name == self.source_field_name:
                # Don't send the signal when we are deleting the
                # duplicate data row for symmetrical reverse entries.
                signals.m2m_changed.send(sender=rel.through, action="pre_remove",
                    instance=self.instance, reverse=self.reverse,
                    model=self.model, pk_set=old_ids)
            # Remove the specified objects from the join table
            db = router.db_for_write(self.through.__class__, instance=self.instance)
            self.through._default_manager.using(db).filter(**{
                source_field_name: self._pk_val,
                '%s__in' % target_field_name: old_ids
            }).delete()
            if self.reverse or source_field_name == self.source_field_name:
                # Don't send the signal when we are deleting the
                # duplicate data row for symmetrical reverse entries.
                signals.m2m_changed.send(sender=rel.through, action="post_remove",
                    instance=self.instance, reverse=self.reverse,
                    model=self.model, pk_set=old_ids)
于 2011-01-18T01:43:52.247 回答
8

根据当前文档,有一个through属性可以让您访问管理多对多关系的表,就像这样Model.m2mfield.through.objects.all()

所以就你的例子而言:

source.items.through.objects \
    .filter(item__timestamp__lte=some_datetime) \
    .delete()
于 2020-10-21T20:32:05.950 回答