Django如何基于admin用户/组权限过滤SQL查询结果生成HTML页面
Django 按用户/用户组动态过滤查询结果实现方案
1. 新建可后台配置的权限规则模型
要实现admin后台可视化配置过滤规则,首先在对应app的models.py中新增规则存储模型,支持绑定单个用户或用户组,存储对应的SQL过滤片段:
# models.py from django.db import models from django.contrib.auth.models import User, Group class DataQueryFilterRule(models.Model): # 规则可单独绑定用户,也可绑定用户组,两类绑定互斥即可 user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, verbose_name="关联用户") group = models.ForeignKey(Group, on_delete=models.CASCADE, null=True, blank=True, verbose_name="关联用户组") # 存储SQL过滤条件片段,不需要带WHERE前缀,例如 email LIKE '%@bait.com' filter_condition = models.TextField(verbose_name="SQL过滤条件") priority = models.IntegerField(default=10, verbose_name="生效优先级,数字越小优先级越高") is_active = models.BooleanField(default=True, verbose_name="规则是否启用") class Meta: verbose_name = "数据查询权限规则" verbose_name_plural = verbose_name ordering = ['priority']
将模型注册到admin后台,即可在后台可视化增删改规则,在admin.py添加如下代码:
# admin.py from django.contrib import admin from .models import DataQueryFilterRule @admin.register(DataQueryFilterRule) class DataQueryFilterRuleAdmin(admin.ModelAdmin): list_display = ['id', 'user', 'group', 'filter_condition', 'priority', 'is_active'] list_filter = ['is_active', 'group'] search_fields = ['user__username', 'filter_condition']
配置完成后执行python manage.py makemigrations和python manage.py migrate完成数据表创建。
2. 改造查询逻辑动态拼接过滤条件
原有的硬编码SQL逻辑需要调整,先获取当前登录用户匹配的所有生效规则,再将规则拼接为WHERE子句,同时添加登录校验避免匿名用户访问,修改views.py代码如下:
# views.py from django.http import HttpResponse, HttpResponseForbidden from django.db import connection from django.contrib.auth.decorators import login_required from .models import DataQueryFilterRule def get_user_match_filters(user): """拉取当前用户匹配的所有生效过滤条件""" conditions = [] # 优先加载绑定到用户本人的规则 user_level_rules = DataQueryFilterRule.objects.filter( user=user, is_active=True ) for rule in user_level_rules: conditions.append(rule.filter_condition) # 再加载用户所属用户组绑定的规则 user_owned_groups = user.groups.all() group_level_rules = DataQueryFilterRule.objects.filter( group__in=user_owned_groups, is_active=True, user__isnull=True ) for rule in group_level_rules: conditions.append(rule.filter_condition) return conditions def query(user): base_sql = "SELECT some, stuff FROM here" match_filters = get_user_match_filters(user) # 无匹配规则默认返回无权限,避免越权查看全量数据 if not match_filters: return None # 拼接最终执行SQL final_sql = f"{base_sql} WHERE {' AND '.join(match_filters)};" with connection.cursor() as cursor: cursor.execute(final_sql) query_result = cursor.fetchall() return query_result # 登录校验装饰器,未登录用户自动跳转登录页 @login_required def index(request): data = query(request.user) if data is None: return HttpResponseForbidden("当前账号未配置数据访问权限,请联系管理员") # 渲染为HTML表格返回,复杂页面建议使用Django模板引擎实现自动转义 html = """ <table border="1" cellpadding="8" cellspacing="0"> <thead><tr><th>some字段</th><th>stuff字段</th></tr></thead> <tbody> """ for line in data: html += f"<tr><td>{line[0]}</td><td>{line[1]}</td></tr>" html += "</tbody></table>" return HttpResponse(html)
3. 规则配置示例
以BAIT用户组的过滤规则为例:
- 先在Django自带的用户组管理中创建BAIT组,将需要归属该组的用户加入组内
- 在admin后台新增一条数据查询权限规则:关联组选择BAIT组,过滤条件填
email LIKE '%@bait.com',设置启用状态 - 配置完成后,所有属于BAIT组的用户访问接口时,会自动拼接该过滤条件,仅返回符合邮箱规则的数据;如果给单个用户配置了专属规则,会和组规则同时生效,取交集结果。
注意事项
- 规则编辑权限仅开放给超级管理员,禁止普通用户接触规则配置入口,避免SQL注入风险
- 如果需要更灵活的规则配置,可扩展规则模型,将字段名、匹配规则、匹配值、逻辑连接符拆分为独立字段,代码层面动态拼接SQL,不需要直接存储SQL片段,安全性更高
- 手动拼接HTML时要对返回的字段内容做转义,避免XSS漏洞,推荐使用Django模板渲染页面,默认自带转义能力
- 如果业务上存在固定的全局过滤条件,可直接和动态获取的用户/组规则一起拼接到WHERE子句中即可。
内容的提问来源于stack exchange,提问作者Harley
相关产品推荐
相关产品推荐

