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

时间:2022-10-04 13:56:40

I'm trying to implement a geofencing for a fleet of trucks. I have to associate a list of boundaries to a vehicle. On top of that one of the requirements is keep everything even once it is deleted for audit purposes. Therefore we have to implement soft delete on everything. This is where the problem lies. My many to many field does not conform to the soft delete manager, it includes both the active and the inactive records in the lookup dataset.

我正在尝试为一队卡车实施地理围栏。我必须将边界列表与车辆相关联。除此之外,其中一个要求是将所有内容删除,以便进行审计。因此,我们必须对所有内容实施软删除。这就是问题所在。我的多对多字段不符合软删除管理器,它包括查找数据集中的活动和非活动记录。

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)

As you see above I tried to make sure the default manager is a soft delete manager (ie. filter for active records only) and also try use limit limit_choices_to but that turn out to field the foreign model only not the "through" model I wanted. If you have any suggestions or recommendation I would love to hear from you.

如上所示,我试图确保默认管理器是一个软删除管理器(即仅针对活动记录的过滤器),并尝试使用limit limit_choices_to但结果只是对外部模型的字段而不是我想要的“直通”模型。如果您有任何建议或建议,我很乐意听取您的意见。

Thanks!

2 个解决方案

#1


5  

First problem: your use of limit_choices_to won't work because as the documentation says:

第一个问题:你使用limit_choices_to将无法正常工作,因为文档说:

limit_choices_to has no effect when used on a ManyToManyField with a custom intermediate table specified using the through parameter.

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

You are using through so limit_choices_to has no effect.

您正在使用,因此limit_choices_to无效。

Second problem: your use of use_for_related_fields = True is also ineffective. The documentation says about this attribute:

第二个问题:你使用use_for_related_fields = True也是无效的。文档说明了这个属性:

If this attribute is set on the default manager for a model (only the default manager is considered in these situations), Django will use that class whenever it needs to automatically create a manager for the class.

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

Your custom manager is assigned to the alive attribute of VehicleBoundaryMap rather than objects so it is ignored.

您的自定义管理器被分配给VehicleBoundaryMap的alive属性而不是对象,因此会被忽略。

The one way I see which may work would be:

我认为哪种方式可行的方法是:

  1. Create a proxy model for VehicleBoundaryMap. Let's call it VehicleBoundaryMapProxy. Set it so that its default manager is SoftDeleteManager() Something like:

    为VehicleBoundaryMap创建代理模型。我们称之为VehicleBoundaryMapProxy。设置它以使其默认管理器是SoftDeleteManager()类似于:

    class VehicleBoundaryMapProxy(VehicleBoundaryMap):
        class Meta:
            proxy = True
    
        objects = SoftDeleteManager()
    
  2. Have through='VehicleBounddaryMapProxy' on your ManyToManyField:

    在ManyToManyField上通过='VehicleBounddaryMapProxy':

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

#2


1  

What about if you just do:

如果你这样做怎么办:

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

And now instead of vehicle.routes.clear() use vehicle.vehicleboundarymap_set.delete(). You will only lose the reverse relation (RouteBoundary.vehicles) but you can implement it back using the same fashion.

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

The rest of the M2M field features are disabled anyway because of the intermediate model.

由于中间模型,其余的M2M场特征无论如何都被禁用。

#1


5  

First problem: your use of limit_choices_to won't work because as the documentation says:

第一个问题:你使用limit_choices_to将无法正常工作,因为文档说:

limit_choices_to has no effect when used on a ManyToManyField with a custom intermediate table specified using the through parameter.

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

You are using through so limit_choices_to has no effect.

您正在使用,因此limit_choices_to无效。

Second problem: your use of use_for_related_fields = True is also ineffective. The documentation says about this attribute:

第二个问题:你使用use_for_related_fields = True也是无效的。文档说明了这个属性:

If this attribute is set on the default manager for a model (only the default manager is considered in these situations), Django will use that class whenever it needs to automatically create a manager for the class.

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

Your custom manager is assigned to the alive attribute of VehicleBoundaryMap rather than objects so it is ignored.

您的自定义管理器被分配给VehicleBoundaryMap的alive属性而不是对象,因此会被忽略。

The one way I see which may work would be:

我认为哪种方式可行的方法是:

  1. Create a proxy model for VehicleBoundaryMap. Let's call it VehicleBoundaryMapProxy. Set it so that its default manager is SoftDeleteManager() Something like:

    为VehicleBoundaryMap创建代理模型。我们称之为VehicleBoundaryMapProxy。设置它以使其默认管理器是SoftDeleteManager()类似于:

    class VehicleBoundaryMapProxy(VehicleBoundaryMap):
        class Meta:
            proxy = True
    
        objects = SoftDeleteManager()
    
  2. Have through='VehicleBounddaryMapProxy' on your ManyToManyField:

    在ManyToManyField上通过='VehicleBounddaryMapProxy':

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

#2


1  

What about if you just do:

如果你这样做怎么办:

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

And now instead of vehicle.routes.clear() use vehicle.vehicleboundarymap_set.delete(). You will only lose the reverse relation (RouteBoundary.vehicles) but you can implement it back using the same fashion.

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

The rest of the M2M field features are disabled anyway because of the intermediate model.

由于中间模型,其余的M2M场特征无论如何都被禁用。