Code

Opened 2 years ago

Closed 2 years ago

Last modified 19 hours 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

Attachments (0)

Change History (3)

comment:1 Changed 2 years ago by ramiro

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset
  • Resolution set to duplicate
  • Status changed from new to closed

Duplicate of #14891.

comment:2 Changed 13 months ago by anonymous

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 Changed 19 hours ago by anonymous

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.

Add Comment

Modify Ticket

Change Properties
<Author field>
Action
as closed
as The resolution will be set. Next status will be 'closed'
The resolution will be deleted. Next status will be 'new'
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.