fredperk
fredperk

Reputation: 818

Django - pre_delete signal not updating instance's foreign key field

I am trying to use a pre_delete signal for the Like model of my Django app. The Like model has a Book foreign key. The Book model has a num_of_likes field. Ultimately, I am trying to update this num_of_likes field from my pre_delete signal. Just I couldn't do it.

My code will make what the problem is very clear, I think (please pay special attention to the comments and print statements):

books/models.py:

class Book(models.Model):
    num_of_likes = models.IntegerField()

likes/models.py:

class Like(models.Model):
    user = models.ForeignKey(User)
    book = models.ForeignKey(Book)

likes/views.py:

class DeleteLikeView(APIView):

    def post(self, request, book):
        book = get_object_or_404(Book, id=book)
        print(book.num_of_likes) # Prints, say, 10
        like = Like.objects.get(user=request.user, book=book)
        like.delete() # triggers signal handler below (should update `book.num_of_likes`)
        print(book.num_of_likes) # Still prints 10, expected 9 <------ PROBLEM
        return ...

likes/signals.py:

@receiver(pre_delete, sender=Like)
def delete_book_like(sender, instance, **kwargs):
    print(instance.book.num_of_likes) # Prints 10
    instance.book.num_of_likes -= 1
    instance.book.save()
    print(instance.book.num_of_likes) # Prints 9, as expected

Why does book.num_of_likes get updated inside delete_book_like but then changes don't show up in DeleteLikeView?

Upvotes: 0

Views: 442

Answers (1)

user2390182
user2390182

Reputation: 73470

The book in your delete view is a different Python object than the instance.book in your signal handler. The Python object does not magically learn that the underlying db representation has changed. You could call refresh_from_db before printing:

 book.refresh_from_db()
 print(book.num_of_likes)

Or just make num_of_likes an dynamically evaluated property altogether and you won't have to worry about the integrity of your denormalized data:

class Book(models.Model):
    @property
    def num_of_likes(self):
        return self.like_set.count()

Upvotes: 3

Related Questions