Django跨多层外键的权限遍历与查询实现咨询
原实现的问题说明
你的逐层过滤思路逻辑上是通的,但有两个明显问题:
- 代码有语法错误:最后一步过滤写的是
Q(recipe__in==recipe),既用了错误的双等号,也引用了不存在的变量recipe(上一步定义的变量名是recipes),代码无法正常运行。 - 存在性能隐患:如果手动逐层查询时提前把中间QuerySet转成列表求值,会产生多次数据库往返,数据量大的时候大量ID塞进
IN子句性能会明显下降;就算不手动求值,靠Django懒加载转成嵌套子查询,代码也过于冗余,很容易写出笔误。
另外你写的contributors__in=request.user是冗余写法,判断多对多关系包含当前用户直接写等值过滤即可,没必要给__in传单个对象。
最优实现方案
直接用Django ORM原生的跨关系双下划线查询就行,不用手动拆分查询层级。ORM会自动把跨关系过滤转成高效的JOIN查询,一次数据库请求就能拿到所有符合权限的数据,哪怕关联层级更深也不会有额外性能损耗。
过滤条件顺着外键关联链路写:Step关联Recipe的外键是recipe → Recipe关联Collection的外键是collection → Collection关联贡献者的多对多字段是contributors,整条过滤链路直接在Step的QuerySet上就能写完。
正确代码如下:
from django.db.models import Q def someViewOfSteps(request): steps = models.Step.objects.filter( # 跨三层关系直接做权限过滤 recipe__collection__contributors=request.user ) # 如果后续逻辑要访问关联的Recipe、Collection字段,加上select_related预加载,避免N+1查询 # steps = steps.select_related("recipe", "recipe__collection") # 要是你之前提到的Collection还有所有者(owner)权限,补个或条件就行 # steps = steps.filter( # Q(recipe__collection__contributors=request.user) | Q(recipe__collection__owner=request.user) # ) return steps
性能说明
上面的写法生成的SQL会通过INNER JOIN关联step、recipe、collection、CollectionContributor中间表,只执行一次数据库查询就返回所有符合权限的Step数据,没有多次数据库往返的开销,比手动拆成多层查询效率高很多,代码也更简洁好维护。
后续如果关联层级继续加深,只要按照外键字段名__下一层关联字段名的规则继续拼接过滤条件就行,不用修改整体查询结构。
内容的提问来源于stack exchange,提问作者SimaPro
相关产品推荐
相关产品推荐

