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

基于用户组/权限限制QuerySet的最佳实践探讨

基于用户权限限制Django QuerySet的最佳实践

兄弟,先给你划个核心红线:绝对不要在前端做数据过滤!前端限制只是“表面功夫”,用户随便用Postman或者浏览器开发者工具直接调用API,就能绕过前端拿到所有发票数据,敏感信息泄露的风险极高,这是绝对的禁忌。

接下来聊聊DRF里常用的后端解决方案,都是业内公认的最佳实践:

1. 自定义权限类 + 重写get_queryset(最常用、最标准)

这是DRF开发中最主流的做法,把权限校验和数据范围控制分开,逻辑清晰又安全:

第一步:写权限类,校验用户资格

先定义一个权限类,判断用户是Admin(能看所有)还是Broker(只能看自己的),同时阻止无关用户访问:

from rest_framework import permissions

class IsAdminOrOwnInvoice(permissions.BasePermission):
    def has_object_permission(self, request, view, obj):
        # Admin组用户拥有所有权限
        if request.user.groups.filter(name='Admin').exists():
            return True
        # Broker只能访问自己关联的发票
        return obj.user == request.user

    def has_permission(self, request, view):
        # 先确保用户是Admin或Broker,否则直接拒绝访问
        return request.user.groups.filter(name__in=['Admin', 'Broker']).exists()

第二步:在视图里过滤QuerySet

在视图集里重写get_queryset方法,根据用户身份返回对应的数据范围,从根源上限制返回的内容:

from rest_framework import viewsets
from .models import Invoice
from .serializers import InvoiceSerializer
from .permissions import IsAdminOrOwnInvoice

class InvoiceViewSet(viewsets.ModelViewSet):
    serializer_class = InvoiceSerializer
    permission_classes = [IsAdminOrOwnInvoice]

    def get_queryset(self):
        queryset = Invoice.objects.all()
        # 如果不是Admin,只返回当前用户的发票
        if not self.request.user.groups.filter(name='Admin').exists():
            queryset = queryset.filter(user=self.request.user)
        return queryset

这种方式的好处是:权限类负责“能不能访问”,get_queryset负责“能看到什么”,分工明确,代码也容易维护。

2. 自定义Model Manager(复用性更强)

如果多个视图都需要用到相同的发票过滤逻辑,把逻辑封装到模型的Manager里会更优雅,避免重复代码:

from django.db import models

class InvoiceManager(models.Manager):
    def for_user(self, user):
        # 根据用户身份返回对应的QuerySet
        if user.groups.filter(name='Admin').exists():
            return self.all()
        return self.filter(user=user)

class Invoice(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    # 其他字段...
    objects = InvoiceManager()

然后在视图里直接调用这个方法就行:

def get_queryset(self):
    return Invoice.objects.for_user(self.request.user)

把业务逻辑下沉到模型层,视图层会更简洁,适合多视图复用的场景。

3. 自定义FilterBackend(适合复杂过滤场景)

如果需要结合其他过滤条件(比如按日期、发票状态过滤),可以用DRF的FilterBackend来封装权限过滤逻辑:

from rest_framework.filters import BaseFilterBackend

class InvoicePermissionFilter(BaseFilterBackend):
    def filter_queryset(self, request, queryset, view):
        if request.user.groups.filter(name='Admin').exists():
            return queryset
        return queryset.filter(user=request.user)

然后在视图里配置这个FilterBackend,还能和DRF自带的SearchFilter、OrderingFilter配合使用:

class InvoiceViewSet(viewsets.ModelViewSet):
    serializer_class = InvoiceSerializer
    permission_classes = [permissions.IsAuthenticated]
    filter_backends = [InvoicePermissionFilter, filters.SearchFilter]
    search_fields = ['invoice_number']
    queryset = Invoice.objects.all()

这种方式适合把权限过滤和其他业务过滤解耦,扩展性更强。

关于你提到的“两个权限”思路

你说的创建can_see_all_invoices和can_see_own_invoices两个权限其实也可行,但没必要拆分这么细——一个权限类就能覆盖两种场景,代码更简洁。如果未来有更复杂的角色权限需求,可以结合Django自带的权限系统(比如permission_required装饰器),但对于这种按用户组区分的场景,直接判断组身份会更直观。

最后再强调一遍:前端过滤绝对不可取,所有权限控制和数据过滤必须在后端完成,这是保障数据安全的核心原则。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 16:52:56