DRF过滤实现:工单消息按主键过滤及专属访问权限控制方案
问题解决方法
1. 按URL传入的工单主键过滤关联消息
DRF的通用视图可以通过重写get_queryset方法,从URL参数中获取工单ID后过滤查询集即可,两个消息视图都需要做对应修改:
class TicketAllMessagesSerializerView(generics.ListAPIView): queryset = TicketMessage.objects.all() serializer_class = TicketMessageSerializer permission_classes = [permissions.IsAuthenticated] # 新增过滤逻辑 def get_queryset(self): ticket_pk = self.kwargs.get("pk") return super().get_queryset().filter(ticket_id=ticket_pk) class TicketMessagesSerializerView(generics.ListAPIView): queryset = TicketMessage.objects.all() serializer_class = TicketMessageSerializer pagination_class = TicketMessagesPagination permission_classes = [permissions.IsAuthenticated] # 新增过滤逻辑 def get_queryset(self): ticket_pk = self.kwargs.get("pk") return super().get_queryset().filter(ticket_id=ticket_pk)
如果担心参数命名混淆,也可以把路由中的<int:pk>改为<int:ticket_pk>,对应代码中取参数的key同步修改即可。
2. 工单消息访问权限控制
自定义DRF权限类,匹配三类用户的访问规则后,将权限类绑定到对应视图即可:
第一步:编写自定义权限类
from rest_framework.permissions import BasePermission class TicketAccessPermission(BasePermission): def has_permission(self, request, view): ticket_pk = view.kwargs.get("pk") # 先校验工单是否存在,避免泄露工单存在性 try: ticket = Ticket.objects.only("user_id", "support_id").get(pk=ticket_pk) except Ticket.DoesNotExist: return False # 匹配三类可访问用户 return ( request.user.is_superuser or request.user.id == ticket.user_id or request.user.id == ticket.support_id )
第二步:绑定权限到视图
修改两个消息视图的permission_classes配置:
permission_classes = [permissions.IsAuthenticated, TicketAccessPermission]
该权限类也可以复用到工单详情、工单状态修改等其他需要工单权限校验的接口中。
现有代码优化建议
- 视图逻辑优化:当前
TicketSerializerView继承的是ListAPIView,仅支持列表查询,你写的create方法不会生效,若需要支持创建工单,改为继承generics.ListCreateAPIView,同时重写perform_create方法自动绑定当前登录用户为工单创建人,避免前端伪造用户ID提交。 - 模型字段优化:
TicketMessage的read字段改为BooleanField更符合业务语义,type字段的选项用Django内置的TextChoices定义更规范;Ticket模型的user字段related_name命名为user不合理,改为created_tickets更便于反向查询;ImageField不要用空字符串做默认值,改为null=True, blank=True避免存储异常。 - 序列化器优化:不要统一用
fields = '__all__',明确指定返回字段避免敏感信息泄露;TicketMessageSerializer也可以添加SlugRelatedField返回用户名,避免只返回用户ID增加前端适配成本;需要前端提交的外键字段(如工单主题、状态)可以设置可写的SlugRelatedField并指定queryset,允许前端传名称而非ID,接口更友好。 - 性能优化:
TicketAllMessagesSerializerView全量返回工单消息在消息量大时会有性能问题,建议取消该接口,统一用分页接口,特殊场景需要全量的也要限制查询字段、增加数量上限。
内容的提问来源于stack exchange,提问作者Konstantin Skokov
相关产品推荐
相关产品推荐

