I have the following model:
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)
my view:
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)
The problems are:
- I know about the F expression, I'll use it down below, but I want to know another ways of updating field and not updating the updated_at field.
- I want to save the changes of model's
views
field but everytime I save it, it sets the new value toupdated_at
field. It means everytime I refresh the page it shows the current time on updated_at field, which is very bad.
I have changed my view's code to:
...
def get(self, *args, **kwargs):
Announcement.objects.filter(id=kwargs['id']).update(views=F('views') 1)
return super().get(self, *args, **kwargs)
Now it works fine but I have several questions. How it saves the new value of views
field without calling the save method of """"Django"""""? How far can I go with it. What is the best practices of changing some fields of model without hitting the save method?
CodePudding user response:
An easy way to do this, might be to specify update_fields
in the .save(…)
method [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',))
It is however better to work with an F
-expression to avoid race-conditions.
Making use of:
Announcement.objects.filter(id=kwargs['id']).update(views=F('views') 1)
will let the database make a query that looks like:
UPDATE appname_announcement SET view = view 1 WHERE id = id
If you thus do not have to load the object in memory, this will be more efficient, since it makes a single roundtrip to the database.