Rox Rosales
Rox Rosales

Reputation: 155

I would like to manipulate the data from the views to / render (html) in Django

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

Answers (1)

Jacinator
Jacinator

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

Related Questions