Reputation: 8098
I am changing a list of field's values upon viewing, but I want to pass the unchanged values to the context. The case here is of a primitive notification system where upon viewing the notification, it should change its status to viewed.
views.py
class Notification(TemplateView):
template_name='myapp/notification.html'
def get_context_data(self, **kwargs):
user = self.request.user
user_unread = user.notification_set.filter(viewed=False)
user_read = user.notification_set.filter(viewed=True)
context = super(Notification, self).get_context_data(**kwargs)
context.update({'user_unread': user_unread, 'user_read': user_read})
for msg in user_unread:
msg.viewed = True
msg.save()
return context
The Problem with this code however, is that I am getting duplicated values in the read and unread lists, even though I have saved the new values to the model after updating the context that is passed to the template.
template:
Unread:
<ul>
{% for msg in user_unread %}
<li> {{ msg }} </li>
{% endfor %}
</ul>
Already read:
<ul>
{% for msg in user_read %}
<li> {{ msg }} </li>
{% endfor %}
</ul>
On a sidenote, I am new to CBVs and if there if my view code above could be improved I'd love some pointers.
Upvotes: 0
Views: 66
Reputation: 53376
You can try getting another duplicate query set to update the objects. Use non-updated for template context and update only another one.
Like:
def get_context_data(self, **kwargs):
user = self.request.user
user_unread = user.notification_set.filter(viewed=False)
#Do some operation on qs so that it gets evaluated, like
nitems = len(user_unread)
#get another queryset to update
user_unread_toupdate = user.notification_set.filter(viewed=False)
user_read = user.notification_set.filter(viewed=True)
context = super(Notification, self).get_context_data(**kwargs)
context.update({'user_unread': user_unread, 'user_read': user_read})
#Use 2nd queryset
for msg in user_unread_toupdate:
msg.viewed = True
msg.save()
return context
Django would cache for each queryset differently. So once evaluated user_unread
will have its own copy of objects.
Although, its not very elegant/efficient as multiple copies of similar queryset are loaded, so if number of records high, it will be slower.
Upvotes: 1
Reputation: 599866
This is due to the lazy nature of querysets. A queryset does not actually touch the database until you evaluate it, which usually occurs when you iterate. So in your code, you iterate through user_unread
in the view, to set the read status: so the contents of the queryset are fixed at that point. But you don't iterate through user_read
until you reach the template, so the query is not made until then, after you have updated all the unread notifications.
The way to fix this is to explicitly evaluate the read queryset in the view, before you update the unread ones. You can do this by simply calling list
on it:
context.update({'user_unread': user_unread, 'user_read': list(user_read)})
for msg in user_unread:
...
Upvotes: 1