在Django REST中增加计数器字段的最佳位置

时间:2015-09-19 10:15:30

标签: python django django-rest-framework

假设我在Django中有两个Models

图书:

class Book(models.Model):
    title = models.CharField(max_length=100, blank=False)
    number_of_readers = models.PositiveIntegerField(default=0)

阅读器:

class Reader(models.Model):
    book = models.ForeignKey(Book)
    name_of_reader = models.CharField(max_length=100, blank=False)

每次我向数据库添加新的Reader时,我希望将number_of_readers模型中的Book增加1.我不想动态计算行数{{1}出于性能原因,与特定Reader相关的行。

哪里是增加Book字段的最佳位置?在number_of_readersSerializer?我应该使用什么方法?我应该覆盖Model中的.save吗?或Model中的其他内容?

如果有人可以提供关于如何在发布新Serializer的帖子时修改Book表格的完整示例,那就更好了。

感谢。

2 个答案:

答案 0 :(得分:4)

我不会在 REST API级别上执行此操作,我会在模型级别上执行此操作,因为此时+1增加将会总是发生,无论它发生在哪里(不仅在你点击特定的REST视图/序列化器时)

Django信号

  

每当我向数据库添加一个新的Reader时,我想将Book模型中的number_of_readers增加1

我实施了一个post_save信号,该信号在创建模型(Reader)时触发

该信号有一个名为created的参数,在创建模型时为True,这比Model.save()覆盖

更方便

示例大纲

from django.db.models.signals import post_save

def my_callback(sender, instance, created, **kwargs):
    if created:
        reader = instance
        book = reader.book
        book.number_of_readers += 1 # prone to race condition, more on that below
        book.save(update_fields='number_of_readers') # save the counter field only

post_save.connect(my_callback, sender=your.models.Reader)

https://docs.djangoproject.com/en/1.8/ref/signals/#django.db.models.signals.post_save

竞争条件

在上面的代码片段中,如果您想避免竞争条件(当许多线程更新同一个计数器时可能会发生这种情况),您也可以使用F表达式替换book.number_of_readers += 1部分{{ 1}},它使数据库级别的读/写而不是Python,

F('number_of_readers') + 1

此处更多内容:https://docs.djangoproject.com/en/1.8/ref/models/expressions/#avoiding-race-conditions-using-f

如果您想要支持" unreading"还有一个book.number_of_readers = F('number_of_readers') + 1 book.save(update_fields='number_of_readers') 信号来反转计数器。一本书:)

批量或定期更新

如果您希望批量导入读者,或者需要定期更新(或"重排")读者计数(例如每周一次),您可以在上述之外,实现一个重新计算读者并更新post_delete

的函数

答案 1 :(得分:1)

这取决于您的应用程序的设计,特别是您将重用此逻辑的位置。

例如,如果您想在应用中的任何位置添加Reader相同的逻辑,请在信号中执行,如bakkal建议或save。如果它取决于API端点,您可能希望在视图中执行此操作。

这也取决于您是否正在批量插入读者:如果您在savepre_ / post_save中执行此操作,则无法进行批量更新,因此它会最好在QuerySet createbulk_create方法等中执行此操作。

从性能的角度来看,无论您在何处执行此操作,都可能需要使用F表达式:

book.number_of_readers = F('number_of_readers') + added_readers_count