如何过滤多对多字段的模型?

Posted

技术标签:

【中文标题】如何过滤多对多字段的模型?【英文标题】:How to filter through Model of a many-to-many field? 【发布时间】:2016-10-11 14:37:40 【问题描述】:

我正在尝试为卡车车队实施地理围栏。我必须将边界列表与车辆相关联。最重要的是,即使出于审计目的而删除所有内容,也要保留所有内容。因此,我们必须对所有内容实施软删除。这就是问题所在。我的多对多字段不符合软删除管理器,它包括查找数据集中的活动和非活动记录。

class Vehicle(SoftDeleteModel):
    routes = models.ManyToManyField('RouteBoundary', through='VehicleBoundaryMap', verbose_name=_('routes'),
                                    limit_choices_to='active': True)


class VehicleBoundaryMap(SoftDeleteModel):
    vehicle = models.ForeignKey(Vehicle, verbose_name="vehicle")
    route_boundary = models.ForeignKey(RouteBoundary, verbose_name="route boundary")
    # ... more stuff here

    alive = SoftDeleteManager()


class SoftDeleteManager(models.Manager):

    use_for_related_fields = True

    def get_queryset(self):
        return SoftDeleteQuerySet(self.model).filter(active=True)

正如您在上面看到的,我尝试确保默认管理器是软删除管理器(即仅过滤活动记录)并尝试使用限制 limit_choices_to 但结果证明只使用外部模型而不是“通过”我想要的模型。如果您有任何建议或建议,我很乐意听取您的意见。

谢谢!

【问题讨论】:

与其自己实现软删除,不如使用django-reversion之类的东西。 那个软删除已经到处实现了,我不能再改变它了。我希望我们可以使用回归,它可以让我们避免很多头痛。 【参考方案1】:

第一个问题:你不能使用limit_choices_to,因为documentation says:

limit_choices_to 用于带有使用through 参数指定的自定义中间表的ManyToManyField 时无效。

您正在使用through,所以limit_choices_to 无效。

第二个问题:你使用use_for_related_fields = True也是无效的。 documentation 提到了这个属性:

如果在模型的 default 管理器上设置此属性(在这些情况下仅考虑默认管理器),Django 将在需要自动为该类创建管理器时使用该类.

您的自定义管理器被分配给VehicleBoundaryMapalive 属性而不是objects,因此它被忽略了。

我认为可行的一种方法是:

    VehicleBoundaryMap 创建一个proxy model。我们称之为VehicleBoundaryMapProxy。设置它,使其默认管理器为SoftDeleteManager() 类似:

    class VehicleBoundaryMapProxy(VehicleBoundaryMap):
        class Meta:
            proxy = True
    
        objects = SoftDeleteManager()
    

    在您的ManyToManyField 上有through='VehicleBounddaryMapProxy'

     class Vehicle(SoftDeleteModel):
        routes = models.ManyToManyField('RouteBoundary', 
                                         through='VehicleBoundaryMapProxy', 
                                         verbose_name=_('routes'))
    

【讨论】:

让我试一试,目前看来很有希望。 效果很好,感谢 Louis 的解决方案。【参考方案2】:

如果你只是这样做呢:

class Vehicle(SoftDeleteModel):
    #you can even remove that field
    #routes = models.ManyToManyField('RouteBoundary', through='VehicleBoundaryMap', verbose_name=_('routes'),
    #                                limit_choices_to='active': True)

    @property
    def routes(self):
        return RouteBoundary.objects.filter(
            vehicleboundarymap__active=True,
            vehicleboundarymap__vehicle=self,
        )

现在用vehicle.vehicleboundarymap_set.delete() 代替vehicle.routes.clear()。您只会失去反向关系(RouteBoundary.vehicles),但您可以使用相同的方式实现它。

由于中间模型,其余的M2M field 功能无论如何都是disabled。

【讨论】:

我喜欢你的想法,它在盒子外面。但是,由于我不能对其使用预取,这对我来说是一个很大的禁忌。

以上是关于如何过滤多对多字段的模型?的主要内容,如果未能解决你的问题,请参考以下文章

如何过滤和访问 Django QuerySet 中的多对多字段?

如何在引导选项卡块中按 django 模板中的多对多字段过滤对象

Django如何过滤多对多字段中的对象,而不是原始查询集

Django 查询集过滤具有相同多对多字段的对象

Django模板过滤2个多对多字段

如何使用 django rest 框架保存多对多字段对象