Given the models
class TaskGroup(models.Model):
name = models.CharField(max_length=256)
class Task(models.Model):
name = models.CharField(max_length=256)
group = models.ForeignKey(TaskGroup, on_delete=models.CASCADE)
completed = models.BooleanField(default=False)
completed_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True)
and the list view
class TaskGroupListView(ListView):
model = TaskGroup
I'd like to display the list of task groups with corresponding tasks. The catch is - I only want to show the tasks that have not been completed or have been completed by the user, or if the user as the attribute user.type == "ADMIN"
set show all groups and all tasks.
Right now I have a template that looks like:
{% for taskgroup in object_list %}
<h1>{{ taskgroup.name }}</h1>
<ul>
{% for task in taskgroup.task_set.all %}
<li>{{ task.name }}</li>
{% endfor %}
</ul>
{% endfor %}
I know that I can modify the queryset of a list view by overriding get_queryset
like:
def get_queryset(self):
if self.request.user == "ADMIN":
return TaskGroup.objects.all()
else:
...
but I'm not sure how to go about filtering the Task
relations on the TaskGroup
s in the else clause.
I've thought about creating a manager subclass for Task
that can filter based on .completed
and .completed_by
that I can use in the template but that seems against the philosophy of Django - I'd like to keep all the logic in the view (this may be way off the mark so please correct me, its been a while since I've touched django/read two scoops of django).
Is there some idiomatic way to do this? Should I discard ListView entirely and write some custom logic? any guidance here is helpful. Thanks.
CodePudding user response:
You can use prefetch_related
with a Prefetch
that uses a custom filtered queryset like this:
from django.db.models import Prefetch, Q
def get_queryset(self):
if self.request.user.is_admin:
return TaskGroup.objects.all()
return TaskGroup.objects.prefetch_related(
Prefetch(
'task_set',
queryset=Task.objects.filter(Q(completed=False) | Q(completed_by=self.request.user))
)
)
This will get all the TaskGroup
s with the related Tasks
s (in task_set
) filtered by those that are not yet completed or are completed by the current user.