Django. Увеличение количества просмотров объекта без изменения его поля updated_at
У меня следующая модель:
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)
Проблемы следующие:
- Я знаю о выражении F, я буду использовать его ниже, но я хочу знать другой способ обновления поля и не обновлять поле updated_at.
- Я хочу сохранить изменения поля модели
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
без вызова метода save из """"Django"""""? Как далеко я могу зайти с этим. Какова лучшая практика изменения некоторых полей модели без вызова метода save?
Простым способом сделать это может быть указание update_fields
в .save(…)
методе [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
-выражением, чтобы избежать race-условий.
Использование:
Announcement.objects.filter(id=kwargs['id']).update(views=F('views') + 1)
позволит базе данных сделать запрос, который выглядит следующим образом:
UPDATE appname_announcement SET view = view + 1 WHERE id = id
Если вам не нужно загружать объект в память, это будет более эффективно, так как он совершает один поход в базу данных.