我有一个带有ForeignKey字段(Realtor
)的模型(BillingTier
),它有一个ManyToManyField(BillingPlan
)。对于每个登录的房地产经纪人,我想检查他们是否有一个计费计划,提供自己的列表自动反馈。以下是模型的简要说明:
class Realtor(models.Model):
user = models.OneToOneField(User)
billing_tier = models.ForeignKey(BillingTier, blank=True, null=True, default=None)
class BillingTier(models.Model):
plans = models.ManyToManyField(BillingPlan)
class BillingPlan(models.Model):
automatic_feedback = models.BooleanField(default=False)
我有一个权限帮助程序,用于检查每个页面加载的用户权限,并拒绝访问某些页面。如果他们的结算计划中没有自动反馈功能,我想拒绝反馈页面。但是,我不确定获取此信息的最佳方式。这是我到目前为止所研究和发现的内容,但在每个页面加载时查询似乎效率低下:
def isPermitted(user, url):
premium = [t[0] for t in user.realtor.billing_tier.plans.values_list('automatic_feedback') if t[0]]
我看到了一些涉及使用filter
(ManyToMany field values from queryset)的解决方案,但我同样不确定是否对每个页面加载使用查询。我必须从房地产经纪人那里获得帐单层ID:bt_id = user.realtor.billing_tier.id
,然后像这样查询模型:
BillingTier.objects.filter(id = bt_id).filter(plans__automatic_feedback=True).distinct()
我认为第二个选项读得更好,但我认为第一个选项会更好,因为我不必导入和查询BillingTier模型。
有没有更好的选择,或者这两个是我能想到的最好的选择?另外,哪个页面加载会更有效?
答案 0 :(得分:2)
根据OP的邀请,这是一个答案。
核心问题是如何基于高度关系数据模型定义有效的权限检查。
第一个变体涉及从评估Django查询集构建Python列表。怀疑当然必须是它对Python解释器进行了不必要的计算。虽然目前尚不清楚这是否可以容忍,如果同时它允许不太复杂的数据库查询(难以评估的权衡),底层数据库查询并不是很简单。
第二种方法涉及通过关系查找获取额外的1:1数据,然后检查任何记录是否满足不同的1:n关系的访问标准。
我们来看看它们。
bt_id = user.realtor.billing_tier.id
:这是获取以下1:n查询的钩子所必需的。它本身确实非常低效。它可以通过两种方式进行优化。
bt_id = user.realtor.billing_tier_id
,因为id
当然存在于billing_tier
中,无需通过关系操作找到。user
对象,可以告诉Django通过select_related
获取和缓存关系数据。因此,如果页面不仅提取user
对象,而且还提取了所需的billing_tier_id
,我们还保存了一个额外的数据库命中。BillingTier.objects.filter(id = bt_id).filter(plans__automatic_feedback=True).distinct()
可以使用Django的exists进行优化,因为这会重新调整数据库和数据库与Python之间的数据流量。prefetch_related
将1:1和1:n查询合并到一个查询中,但判断是否支付更难。值得一试。在任何情况下,都值得安装一个名为Django Debug Toolbar的gem,它允许您分析实现在数据库查询上花费的时间。