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

Hen*_*rik 2 python django django-rest-framework

假设我Models在 Django 中有两个:

书:

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

读者:

class Reader(models.Model):
    book = models.ForeignKey(Book)
    name_of_reader = models.CharField(max_length=100, blank=False)
Run Code Online (Sandbox Code Playgroud)

每次我Reader向数据库添加一个新的时,我想number_of_readersBook模型中增加 1。出于性能原因,我不想动态计算Reader与特定 相关的行数Book

哪里是增加number_of_readers场的最佳位置?在Serializer或在Model?我应该使用什么方法?我应该重写.saveModel?或者里面的其他东西Serializer

如果有人可以提供一个完整的示例,说明Book在发布新的Reader.

谢谢。

bak*_*kal 6

我不会在REST API 级别执行此操作,我会在模型级别执行此操作,因为这样 +1 的增加将始终发生,无论发生在何处(不仅在您点击特定的 REST 视图/序列化程序时)

姜戈信号

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

我想实现post_save的是,当触发模型(阅读器)信号产生

该信号有一个参数称为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)
Run Code Online (Sandbox Code Playgroud)

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

竞争条件

在上面的代码片段中,如果你想避免竞争条件(当许多线程更新同一个计数器时可能发生),你也可以book.number_of_readers += 1用 F 表达式替换该部分F('number_of_readers') + 1,这使得在 DB 级别上进行读/写蟒蛇,

book.number_of_readers = F('number_of_readers') + 1
book.save(update_fields='number_of_readers')
Run Code Online (Sandbox Code Playgroud)

更多关于这里:https : //docs.djangoproject.com/en/1.8/ref/models/expressions/#avoiding-race-conditions-using-f

post_delete如果您曾经想过支持“未读”一本书,那么也有一个信号可以反转计数器:)

批量或定期更新

如果您希望批量导入阅读器,或者需要定期更新(或“重排”)阅读器计数(例如每周一次),除了上述之外,您还可以实现一个功能来重新计算阅读器并更新 Book.number_of_readers