简体   繁体   中英

How to filter through Model of a many-to-many field?

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.

Thanks!

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

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

You are using through so limit_choices_to has no effect.

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

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.

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

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:

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

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

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM