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 asTaskListView
instead of only.TaskView