Home > front end >  Django overriding get_context_data() not rendering in template
Django overriding get_context_data() not rendering in template

Time:06-01

I'm learning to work with CBV's and I am currently working on a simple todo list, I want to get specific user data for specific users. The way I am doing this is by overriding the get_context_data() method in my ListView class, but the data is not showing in my template. I have created multiple users and created multiple tasks, but still does not work even though I can access them through the admin page.

Here is my code:

views.py:

class TaskList(LoginRequiredMixin, ListView):
    model = Task
    template_name = 'app/home.html'
    context_object_name = 'tasks'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['tasks'] = context['tasks'].filter(user=self.request.user)
        context['count'] = context['tasks'].filter(
            complete=False).count()
        return context

models.py:

from django.db import models
from django.contrib.auth.models import User


class Task(models.Model):
    user = models.ForeignKey(
        User, on_delete=models.CASCADE, blank=True, null=True)
    title = models.CharField(max_length=150)
    description = models.TextField(max_length=500)
    complete = models.BooleanField(default=False)
    created = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.title

    # Order by completion
    class Meta:
        ordering = ['complete']

urls.py:

from django.urls import path

from .views import (
    TaskList,
    TaskDetail,
    TaskCreate,
    TaskUpdate,
    TaskDelete,

    UserLogin,
    UserLogout
)

urlpatterns = [
    path('login/', UserLogin.as_view(), name='login'),
    path('logout/', UserLogout.as_view(), name='logout'),


    path('', TaskList.as_view(), name='home'),
    path('task-detail/<int:pk>/', TaskDetail.as_view(), name='task-detail'),
    path('task-create/', TaskCreate.as_view(), name='task-create'),
    path('task-update/<int:pk>/', TaskUpdate.as_view(), name='task-update'),
    path('task-delete/<int:pk>/', TaskDelete.as_view(), name='task-delete')
]

home.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>To Do List</title>
</head>
<body>
    {% if request.user.is_authenticated %}
        <p>{{ request.user }}</p>
        <a href="{% url 'logout' %}">Logout</a>
    {% else %}
        <a href="{% url 'login' %}">Login</a>
    {% endif %}    
    <hr>
    <a href="{% url 'task-create' %}">Add Task</a>
    <table>
        <tr>
            <th>
                Items
            </th>
        </tr>
        {% for task in tasks %}
        <tr>
            <td>
                {{ task }}
            </td>
            <td>
                <a href="{% url 'task-detail' task.id %}">View</a>
            </td>
            <td>
                <a href="{% url 'task-update' task.id %}">Edit</a>
            </td>
            <td>
                <a href="{% url 'task-delete' task.id %}">Delete</a>
            </td>
        </tr>
        {% endfor %}

    </table>
</body>
</html>

I noticed that when I comment out the line: context['tasks'] = context['tasks'].filter(user=self.request.user) the data does show up in my template but not user specific. What does this mean? Also, adding other context does seem to work, but not when I try getting user specific data. I think I'm missing something small but I can't figure it out.

CodePudding user response:

filter() method or any method of querysets must be applied on models not on empty dictionary keys (eg:tasks).

Try this:

Views.py

class TaskList(LoginRequiredMixin, ListView):
    model = Task
    template_name = 'app/home.html'
    context_object_name = 'all_tasks'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['tasks'] = self.model.objects.filter(user=self.request.user)
        context['count'] = self.model.objects.filter(
            complete=False).count()
        return context

all_tasks will give all objects of Task model as it is ListView which gives all instances, whereas tasks which is in context of get_context_data will give user specific tasks.

Then, you can run loop in template.

Another Best Approach:

If you want to only user specific data in the template app/home.html, so you can simply override the get_queryset inside TaskView in the following way:

views.py

class TaskList(LoginRequiredMixin, ListView):
    model = Task
    template_name = 'app/home.html'
    context_object_name = 'tasks'

    def get_queryset(self):
        qs = super().get_queryset()
        return qs.filter(user=self.request.user)

     #for count you have specifiy in get_context_data
     def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['count'] = self.model.objects.filter(complete=False).count()
        return context   

Then, without changing your code in the template file, you will be able to access user specific data.

Note: It is better to use actual view name as the suffix while working with class based views, so it will be better if you name it as TaskListView instead of only TaskView.

  • Related