Django Related Through Models Not Using Soft Delete Model Manager

Viewed 60

i am trying to retrieve only rows in a through model that are not deleted but keep getting all rows including the deleted ones.
learnt it's a bug with using use_for_related_fields in the model manager according to this link: https://code.djangoproject.com/ticket/17746
i am trying to implement a follow/unfollow system like on social media platforms

below is my code sample

class BasicModelQuerySet(models.QuerySet):
    def delete(self):
        return self.update(is_deleted = True, deleted_at = timezone.now())

    def erase(self):
        return super(BasicModelQuerySet, self).delete()


class BasicModelManager(models.Manager):
    use_for_related_fields = True

    def get_queryset(self):
        return BasicModelQuerySet(self.model, self._db).filter(is_deleted = False).filter(deleted_at__isnull = True)


class BasicModel(models.Model):
    deleted_at = models.DateTimeField(blank = True, null = True)
    is_deleted = models.BooleanField(default = False)

    objects = BasicModelManager()
    everything = models.Manager()

    class Meta:
        abstract = True


class Listing(BasicModel):
    watchers = models.ManyToManyField(User, through = 'Watching', related_name = 'watchers')


class Watching(BasicModel):
    user = models.ForeignKey(User, on_delete = models.RESTRICT, related_name = 'watching')
    listing = models.ForeignKey(Listing, on_delete = models.RESTRICT, related_name = 'spectators')

sample tables

id | user   |
-------------
1  | User A |
2  | User B |

id | listing   |
----------------
1  | Listing A |
2  | Listing B |

# watchings table
id | user   | listing   | deleted_at          | is_deleted |
------------------------------------------------------------
1  | User A | Listing A |                     |            |
2  | User B | Listing A | 2022-02-25 11:07:18 | True       |
3  | User B | Listing B |                     |            |
listing = Listing.objects.get(id = 1)
# returns Listing A

listing.watchers.all()
# returns two rows [1, 2] instead of just 1
# what i want is to return only User A because User B is deleted

listing.watchers.through.objects.all()
# returns two rows [1, 3], which is wrong because row 3 is not for Listing A

how do i get to return only rows that are not deleted for a listing? or do i need to change the design and or not use a through model?

1 Answers
  1. following will return all the Users for a listing which are present in your DB, as you are not applying any filter.
listing.watchers.all()
  1. here you seems to make same mistake of not filtering, remember there are entries in DB. you have not deleted them.
#listing.watchers.through.objects.all()
listing.watchers.through.objects.exclude(is_deleted=True)

Apart from it. what I think wrong is that extra Watching table. that could have been meaningful if you would have more fields in that table(though they dont seems to require as you said, it's just follow/unfollow feature)

Related