Django中如何实现特定用户查看其他用户PII时自动匿名化
Django 实现特定用户隐藏其他用户PII的优化方案
问题说明
需要给标记了cant_see_pii=True的用户隐藏其他用户的敏感信息(比如用户名、头像),但自己的信息可以正常查看。数据库里用UserAttributes模型的cant_see_pii字段控制权限,绝对不想在每个模板里重复写if-else判断,所以得找更优雅的实现方式。
对应的模型代码:
class UserAttributes(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, related_name = 'user_attribute') cant_see_pii = models.BooleanField(null=True, blank=True, default = False) profile_picture = models.ImageField("Profile Picture", default='images/default.svg', upload_to='images/', null=True, blank=True)
可行方案
1. 给用户模型加动态属性方法
直接在User模型里扩展方法,根据当前查看者的权限返回处理后的值。建议把ForeignKey改成OneToOneField(毕竟一个用户对应一套属性更合理),然后扩展User模型:
from django.contrib.auth.models import User # 调整模型关联(可选但更合理) class UserAttributes(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='user_attribute') cant_see_pii = models.BooleanField(null=True, blank=True, default=False) profile_picture = models.ImageField("Profile Picture", default='images/default.svg', upload_to='images/', null=True, blank=True) # 给User模型绑定自定义方法 def get_visible_username(self, viewer): # viewer为当前登录的用户 if viewer.user_attribute.cant_see_pii and self != viewer: return f"Anonymous User {self.id}" # 用用户ID做唯一标识,避免重名 return self.username def get_visible_profile_pic(self, viewer): if viewer.user_attribute.cant_see_pii and self != viewer: return 'images/anonymous.svg' # 提前准备好匿名头像路径 return self.user_attribute.profile_picture User.add_to_class('get_visible_username', get_visible_username) User.add_to_class('get_visible_profile_pic', get_visible_profile_pic)
模板里直接调用方法即可,无需写判断逻辑:
<h1>{{ other_user.get_visible_username(user) }}</h1> <img src="{{ other_user.get_visible_profile_pic(user).url }}" alt="Profile">
2. 编写全局模板过滤器
创建自定义模板过滤器,用管道符在模板中调用,复用性更强。
第一步,在你的app目录下新建templatetags/pii_filters.py文件:
from django import template from django.contrib.auth.models import User register = template.Library() @register.filter def anonymize_username(user_obj, viewer): if viewer.user_attribute.cant_see_pii and user_obj != viewer: return f"Anonymous User {user_obj.id[:5]}" # 取ID前5位,显示更简洁 return user_obj.username @register(filter) def anonymize_profile_pic(user_obj, viewer): if viewer.user_attribute.cant_see_pii and user_obj != viewer: return 'images/anonymous.svg' # 处理默认头像的边界情况 return user_obj.user_attribute.profile_picture.url if user_obj.user_attribute.profile_picture else 'images/default.svg'
第二步,在模板中加载过滤器并使用:
{% load pii_filters %} <h1>{{ other_user|anonymize_username:user }}</h1> <img src="{{ other_user|anonymize_profile_pic:user }}" alt="Profile">
3. 视图层提前处理数据
如果是列表页这类批量展示用户的场景,可以在视图里提前处理所有用户的PII数据,再传递给模板,模板直接使用处理后的字段。
以类视图为例:
from django.views.generic import ListView from django.contrib.auth.models import User class UserListView(ListView): model = User template_name = 'user_list.html' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) current_user = self.request.user need_anonymize = current_user.user_attribute.cant_see_pii # 预处理用户数据 processed_users = [] for user in context['object_list']: processed = { 'user': user, 'display_username': user.username if user == current_user or not need_anonymize else f"Anonymous User {user.id}", 'display_profile_pic': user.user_attribute.profile_picture.url if (user == current_user or not need_anonymize) else 'images/anonymous.svg' } processed_users.append(processed) context['processed_users'] = processed_users return context
模板里直接循环处理好的数据:
{% for item in processed_users %} <h1>{{ item.display_username }}</h1> <img src="{{ item.display_profile_pic }}" alt="Profile"> {% endfor %}
4. API场景用自定义序列化器
如果是前后端分离的API,用Django REST Framework的序列化器处理最方便:
from rest_framework import serializers from django.contrib.auth.models import User class UserPIISerializer(serializers.ModelSerializer): username = serializers.SerializerMethodField() profile_picture = serializers.SerializerMethodField() class Meta: model = User fields = ['id', 'username', 'profile_picture'] def get_username(self, obj): viewer = self.context['request'].user if viewer.user_attribute.cant_see_pii and obj != viewer: return f"Anonymous User {obj.id}" return obj.username def get_profile_picture(self, obj): viewer = self.context['request'].user if viewer.user_attribute.cant_see_pii and obj != viewer: return 'images/anonymous.svg' return obj.user_attribute.profile_picture.url if obj.user_attribute.profile_picture else 'images/default.svg'
视图中需将request传递到序列化器上下文:
from rest_framework.response import Response from rest_framework.views import APIView class UserListAPI(APIView): def get(self, request): users = User.objects.all() serializer = UserPIISerializer(users, many=True, context={'request': request}) return Response(serializer.data)
内容的提问来源于stack exchange,提问作者William
相关产品推荐
相关产品推荐

