Django.Increment对象的视图计数,而不影响其updated_at字段

tnkciper  于 2022-11-18  发布在  Go
关注(0)|答案(1)|浏览(129)

我有以下模型:

class Announcement(models.Model):
    ...
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    views = models.PositiveIntegerField(default=0, editable=False)

我观点:

class AnnouncementDetailView(DetailView):
    model = Announcement
    context_object_name = 'announcement'
    template_name = 'web/index.html'

    def get(self, *args, **kwargs):
        announcement = get_object_or_404(Announcement, id=kwargs['id'])
        announcement.views += 1
        announcement.save()
        return super().get(self, *args, **kwargs)

问题是:
1.我知道F表达式,我将在下面使用它,但我想知道更新字段而不更新updated_at字段的另一种方法。
1.我想保存对model的views字段的更改,但是每次保存时,它都会将新值设置为updated_at字段。这意味着每次刷新页面时,它都会在updated_at字段上显示当前时间,这非常糟糕。
我已将视图的代码更改为:

...
def get(self, *args, **kwargs):
    Announcement.objects.filter(id=kwargs['id']).update(views=F('views') + 1)
    return super().get(self, *args, **kwargs)

现在它运行的很好,但是我有几个问题。它如何保存views字段的新值而不调用Django的save方法?我能用它走多远?在不点击save方法的情况下更改模型的一些字段的最佳实践是什么?

zpgglvta

zpgglvta1#

一个简单的方法就是在**.save(…)**方法中指定update_fields [Django-doc]:

class AnnouncementDetailView(DetailView):
    model = Announcement
    context_object_name = 'announcement'
    template_name = 'web/index.html'

    def get(self, *args, **kwargs):
        try:
            return super().get(self, *args, **kwargs)
        finally:
            object = getattr(self, 'object', None)
            if object:
                object.views = F('views') + 1
                object.save(update_fields=('views',))

但是,最好使用F表达式来避免 * 竞态条件 *。
利用:

Announcement.objects.filter(id=kwargs['id']).update(views=F('views') + 1)

将让数据库进行如下查询:

UPDATE appname_announcement
SET **view = view + 1**
WHERE id = id

如果这样就不必将对象加载到内存中,则效率会更高,因为它只对数据库进行一次往返。

相关问题