Opened 12 years ago

Closed 12 years ago

Last modified 10 years ago

#17746 closed Bug (duplicate)

use_for_related_fields=False is not honored for m2m relation

Reported by: mgagne_98@… Owned by: nobody
Component: Database layer (models, ORM) Version: 1.3
Severity: Normal Keywords: use_for_related_fields m2m
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Considering the following model: Trip <-> TripDestination <-> Destination (many to many relation). When I delete a Trip, the SoftDeleteManager filters out all the deleted trip. However, if I request all the destinations of a trip (using get_object_or_404(Trip, pk = id)), I also get the deleted ones (i.e. TripDestination models with deleted_at == null OR deleted_at != null). The SoftDeleteManager should have filtered it out.

Repro Steps

class SoftDeleteManager(models.Manager):
    use_for_related_fields = True

    def get_query_set(self):
        query_set = super(SoftDeleteManager, self).get_query_set()
        return query_set.filter(deleted_at__isnull = True)

class LifeTimeTrackingModel(models.Model):
    created_at = models.DateTimeField(auto_now_add = True)
    updated_at = models.DateTimeField(auto_now = True)
    deleted_at = models.DateTimeField(null = True)

    objects = SoftDeleteManager()
    all_objects = models.Manager()

    class Meta:
        abstract = True

class Destination(LifeTimeTrackingModel):
    city_name = models.CharField(max_length = 45)

class Trip(LifeTimeTrackingModel):
    name = models.CharField(max_length = 250)
    destinations = models.ManyToManyField(Destination, through = 'TripDestination')

class TripDestination(LifeTimeTrackingModel):
    trip = models.ForeignKey(Trip)
    destination = models.ForeignKey(Destination)
  • Create a new Trip object with deleted_at set to null
  • Create a new Destination object with deleted_at set to null
  • Create a new TripDestination object with deleted_at set to datetime.now() linking the previously created Trip and Destionation objects

Actual Result

  • Trip.objects.destinations returns the Destination objects

Expected Result

  • Trip.objects.destinations shouldn't returns the Destination objects due to the SoftDeleteManager logic which should have filter it out

Other
http://stackoverflow.com/questions/9374962/how-to-soft-delete-many-to-many-relation-with-django

Change History (3)

comment:1 by Ramiro Morales, 12 years ago

Resolution: duplicate
Status: newclosed

Duplicate of #14891.

comment:2 by anonymous, 11 years ago

I do not believe this is a duplicate of #14891. The issue in that bug is that use_for_related = False is not respected.

This bug (if it is one) seems to be related to the fact that the manager on the Destination object is used, and the manager on the through model never enters the equation.

i.e., trip_object.destinations.model == Destination

I think the documentation and/or the API design is unclear about this. Is there a way to make trip_object.destinations.all() only return the non-deleted destinations?

comment:3 by anonymous, 10 years ago

I also had problems with this. Now I don't see it as a bug, but as a badly documented API. Related managers are the managers of the remote model, not of a through model. So:

trip_object.destinantions.some_method() calls Destination manager.
destinantion_object.trip_set.some_method() calls Trip manager.
TripDestination manager is not called at any time.

You can call it with trip_object.destinantions.through.objects.some_method(), however.
My application differs, but I may suggest modifing the remote manager to check TripDestination.deleted_at field at its get_queryset.

Note: See TracTickets for help on using tickets.
Back to Top