Reputation: 155
I have this view using generic in django and I need to manipulate it. But it seems that whenever I tried using setattr it doesn't manipulate/replace the data in the queryset.
view.py
class ArticleListView(ListView): #using the Generic ListView in django
template_name = 'article/article_list_view.html'
queryset = Article.objects.all()[:5]
def get(self, request, *args, **kwargs):
setattr(self.queryset[0], 'title', 'something') #Trying to change the title in the first element
print(getattr(self.queryset[0], 'title')) #it outputs the old value
return super().get(request, *args, **kwargs)
article_list_view.html
{% extends 'base.html' %}
{% block content %}
{% for instance in object_list %}
<h5>{{instance.title}}</h5>
{% autoescape off %}
<p>{{instance.content| capfirst}}</p>
{% endautoescape %}
{% endfor %}
{% endblock content %}
Do you have any idea to solve this issue? Or maybe because it was immutable?
Upvotes: 0
Views: 215
Reputation: 1413
This is a challenge because according to Django's docs on querysets "a QuerySet can be constructed, filtered, sliced, and generally passed around without actually hitting the database". The first thing you and I both tried was grabbing the first object out of the queryset and setting the attribute. This doesn't help because the queryset itself isn't being evaluated until you iterate over it: {% for instance in object_list %}
. When that happens the model instance will be retrieved from the database again.
The best solution I can see is to force the queryset to be evaluated before updating the model instance.
class ArticleListView(ListView):
template_name = 'article/article_list_view.html'
queryset = Article.objects.all()[:5]
def get_context_data(self, *, object_list=None, **kwargs):
# Overwriting this method to receive exactly the same arguments as the original
# https://github.com/django/django/blob/3.0.6/django/views/generic/list.py#L113
if object_list is None:
object_list = self.object_list
if not isinstance(object_list, tuple): # Force the queryset into a tuple so it's evaluated
object_list = tuple(object_list)
object_list[0].title = 'something' # Update the instance's title
return super().get_context_data(object_list=object_list, **kwargs)
This does have the downside of article_list
won't be available in the context unless you explicitly set context_object_name = 'article_list'
on your view, but you already weren't using that variable.
I've tested out parts of this code to verify that my theory is right, but I haven't run the code in it's entirety, so you might need to tweak things to get it working exactly as you want it.
Upvotes: 1