Reputation: 3379
My Django project contains a task manager with Projects and Tasks, I have generic list page showing a list of all projects with information on their total tasks:
class IndexView(generic.ListView):
template_name = 'projects/index.html'
context_object_name = 'project_list'
def get_queryset(self):
"""Return 10 projects."""
return Project.objects.order_by('is_complete')[:10]
I would like to display on my list page the total number of added projects and tasks, but I'm unsure how I should go about this. All my current work has been around listing the number of tasks that are included i each project, but now I want a total - should I add this as a new View? For example, I tried adding this to the view above:
def total_projects(self):
return Project.objects.count()
Then calling {{ project_list.total_projects }}
on my template, but it doesn't return anything.
Is Views the correct place to do this?
Upvotes: 3
Views: 696
Reputation: 1243
I believe it's more common to put function definitions in the Model class (Project
, from the looks of it), and add the @property
tag above the function.
class Project(models.Model):
''' definitions and stuff '''
@property
def total_projects(self): # etc...
As for your specific case, you could forego the function altogether and just use {{ project_list.count }}
or {{ project_list|length }}
in your template.
A note about count
vs length
from the docs:
A count() call performs a SELECT COUNT(*) behind the scenes, so you should always use count() rather than loading all of the record into Python objects and calling len() on the result (unless you need to load the objects into memory anyway, in which case len() will be faster).
Note that if you want the number of items in a QuerySet and are also retrieving model instances from it (for example, by iterating over it), it’s probably more efficient to use len(queryset) which won’t cause an extra database query like count() would.
So use the correct one for your usage.
Also, according to this answer and the below comment from @djangomachine, length
may not always return the same number of records as count
. If accuracy is important it may be better to use count
regardless of the above case.
Upvotes: 1
Reputation: 835
All by current work has been around listing the number of tasks that are included i each project, but now I want a total - should I add this as a new View?
It depends. If you just want to show the total number of projects and tasks with the first 10 completed projects in the database (which is what your get_queryset
method does, be careful), I would go and do all of it in the same view (it would be useless to make a new view only to show some numbers, and that isn't the purpose of ListView
IMO).
On the other hand, you're calling a class's instance method (total_projects
) from a model's instance. That method doesn't exists on the model, and when an attribute/method doesn't exists in an object when calling it in a template, you just get nothing. Based on the previous paragraph, I would set it in the view's context using get_context_data:
def get_context_data(self, **kwargs):
data = super().get_context_data(**kwargs)
data["total_projects"] = Projects.objects.count()
# I'm assuming you only need the whole number of tasks with no separation
data["total_tasks"] = Task.objects.count()
return data
Finally, you can edit your get_queryset
method and make it be an instance attribute (if you want it to be cleaner and you can handle the filtering with no additional code):
class IndexView(generic.ListView):
queryset = Project.objects.order_by('is_complete')[:10]
Upvotes: 2