You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django ListView中按用户权限过滤关联Task对象的实现方案

解决方案

方案1:仅在视图层实现(无需修改模型,符合你的需求)

你不需要放弃ListView,Django的Prefetch对象正好可以解决关联模型预过滤的需求,还能避免N+1查询问题,性能更好。

视图代码修改

from django.db.models import Prefetch, Q
from django.views.generic.list import ListView

class TaskGroupListView(ListView):
    model = TaskGroup

    def get_queryset(self):
        user = self.request.user
        base_queryset = TaskGroup.objects.all()
        # 注意你之前的判断逻辑写错了,应该判断user.type属性
        if user.type == "ADMIN":
            return base_queryset.prefetch_related("task_set")
        # 普通用户过滤关联任务
        allowed_task_queryset = Task.objects.filter(
            Q(completed=False) | Q(completed_by=user)
        )
        # 把过滤后的任务绑定到TaskGroup的自定义属性上
        return base_queryset.prefetch_related(
            Prefetch("task_set", queryset=allowed_task_queryset, to_attr="filtered_tasks")
        )

模板代码修改

只需要把遍历任务的部分改成读取我们自定义的属性即可:

{% for taskgroup in object_list %}
<h1>{{ taskgroup.name }}</h1>
<ul>
    {% for task in taskgroup.filtered_tasks %}
    <li>{{ task.name }}</li>
    {% endfor %}
</ul>
{% endfor %}

方案2:自定义模型管理器(更符合Django惯用法)

你之前的认知存在偏差:可复用的查询逻辑封装到模型管理器/模型方法是《Two Scoops of Django》明确推荐的最佳实践,不属于反模式。如果后续其他视图也需要用到相同的任务过滤规则,这种方式可以避免重复代码。

模型修改

class TaskManager(models.Manager):
    def visible_for_user(self, user):
        if user.type == "ADMIN":
            return self.all()
        return self.filter(
            Q(completed=False) | Q(completed_by=user)
        )

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)
    # 替换默认管理器
    objects = TaskManager()

视图修改

和方案1逻辑一致,只是查询逻辑可以复用:

def get_queryset(self):
    user = self.request.user
    base_queryset = TaskGroup.objects.all()
    return base_queryset.prefetch_related(
        Prefetch("task_set", queryset=Task.objects.visible_for_user(user), to_attr="filtered_tasks")
    )

模板部分的修改和方案1完全相同。

额外说明

  • 完全没有必要放弃ListView,通用视图的设计初衷就是允许开发者重写部分方法实现自定义逻辑,从头编写视图反而属于重复造轮子。
  • 如果你实在不想修改模板,也可以重写ListView的get_context_data方法,手动给每个TaskGroup挂载过滤后的任务,但性能会比prefetch的方案差。

内容的提问来源于stack exchange,提问作者Dave Fol

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 11:06:04