Сигнал Django — pre_delete не обновляет поле внешнего ключа экземпляра

#python #django #django-models #django-views #django-signals

#python #django #django-модели #django-просмотры #django-сигналы

Вопрос:

Я пытаюсь использовать pre_delete сигнал для Like модели моего приложения Django. Like Модель имеет Book внешний ключ. В Book модели есть num_of_likes поле. В конечном счете, я пытаюсь обновить это num_of_likes поле из своего pre_delete сигнала. Просто я не мог этого сделать.

Я думаю, что мой код сделает проблему предельно ясной (пожалуйста, обратите особое внимание на комментарии и инструкции печати):

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
  

Почему book.num_of_likes обновляется внутри delete_book_like , но затем изменения не отображаются DeleteLikeView ?

Ответ №1:

book В вашем представлении удаления это другой объект Python, чем instance.book в вашем обработчике сигналов. Объект Python волшебным образом не узнает, что базовое представление БД изменилось. Вы могли бы вызвать refresh_from_db перед печатью:

  book.refresh_from_db()
 print(book.num_of_likes)
  

Или просто создайте num_of_likes динамически оцениваемое свойство в целом, и вам не придется беспокоиться о целостности ваших денормализованных данных:

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

Комментарии:

1. Спасибо за ваше решение. Не могли бы вы указать мне на некоторую документацию, касающуюся декоратора @property, который вы предложили? Спасибо

2. официальные документы всегда хорошее начало. Вас также может заинтересовать cached_property django