You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何扩展Django权限实现教练仅查看同品牌患者?

当然可以!完全不用在每个视图里重复写过滤逻辑,我们可以通过自定义Django权限系统+全局查询过滤的方式来实现这个需求,既优雅又能统一控制。下面是具体的实现步骤:

核心思路

我们要完成两件核心事情:

  • 实现对象级权限控制:确保教练只能访问和自己同品牌的患者对象
  • 实现自动查询集过滤:让列表视图自动只返回同品牌的患者,不用每个视图单独写过滤逻辑

1. 自定义对象级权限后端

Django默认的权限是模型级的,我们需要扩展它来支持对象级的权限判断。在你的app下创建permissions.py文件:

from django.contrib.auth.backends import ModelBackend
from .models import Coach, Patient

class BrandingPermissionBackend(ModelBackend):
    def has_perm(self, user_obj, perm, obj=None):
        # 如果用户不是教练,沿用默认权限逻辑
        if not isinstance(user_obj, Coach):
            return super().has_perm(user_obj, perm, obj)
        
        # 针对查看患者的权限,判断对象是否和教练同品牌
        if perm == 'your_app.view_patient' and obj is not None:
            return user_obj.branding == obj.branding
        
        # 其他权限类型沿用默认逻辑
        return super().has_perm(user_obj, perm, obj)

然后在项目的settings.py里配置这个权限后端,让Django优先使用它:

AUTHENTICATION_BACKENDS = [
    'your_app.permissions.BrandingPermissionBackend',
    'django.contrib.auth.backends.ModelBackend',
]

2. 自定义查询集自动过滤同品牌患者

为了让列表视图自动过滤数据,我们给Patient模型自定义查询集和管理器,这样可以统一调用过滤逻辑:

修改models.py中的Patient模型:

from django.db import models
from django.contrib.auth.models import User

# 先定义查询集
class PatientQuerySet(models.QuerySet):
    def for_coach(self, coach):
        # 过滤出和当前教练同品牌的患者
        return self.filter(branding=coach.branding)

# 再定义管理器
class PatientManager(models.Manager):
    def get_queryset(self):
        return PatientQuerySet(self.model, using=self._db)
    
    def for_coach(self, coach):
        return self.get_queryset().for_coach(coach)

# 修改Patient模型
class Patient(User):
    name = models.CharField(max_length=100)  # 记得给CharField加max_length参数哦
    email = models.EmailField()
    branding = models.CharField(choices=BRANDINGS, max_length=50)
    
    # 替换默认管理器
    objects = PatientManager()

3. 基类视图统一应用逻辑

创建一个基类视图,所有教练访问患者的视图都继承它,这样不用重复写登录验证、权限判断和查询集过滤:

from django.views.generic import ListView, DetailView
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from .models import Patient, Coach

class CoachPatientBaseView(LoginRequiredMixin, UserPassesTestMixin):
    def test_func(self):
        # 确保当前登录用户是教练
        return isinstance(self.request.user, Coach)
    
    def get_queryset(self):
        # 自动返回当前教练同品牌的患者
        return Patient.objects.for_coach(self.request.user)

# 示例:患者列表视图
class PatientListView(CoachPatientBaseView, ListView):
    model = Patient
    template_name = 'patients/patient_list.html'

# 示例:患者详情视图
class PatientDetailView(CoachPatientBaseView, DetailView):
    model = Patient
    template_name = 'patients/patient_detail.html'

注意事项

  • 如果你用的是多表继承(而非代理模型),比如CoachUser的一对一关联模型,那需要调整权限后端里的判断逻辑,比如通过user_obj.coach获取教练实例,再对比品牌。
  • 记得给CharField类型的字段加上max_length参数,不然Django会报错哦。

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

火山引擎 最新活动