如何从Django ForeignKey中检索值 - > ManyToMany领域?

时间:2013-04-29 17:38:49

标签: django

我有一个带有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]]

我看到了一些涉及使用filterManyToMany 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模型。

有没有更好的选择,或者这两个是我能想到的最好的选择?另外,哪个页面加载会更有效?

1 个答案:

答案 0 :(得分:2)

根据OP的邀请,这是一个答案。

核心问题是如何基于高度关系数据模型定义有效的权限检查。

第一个变体涉及从评估Django查询集构建Python列表。怀疑当然必须是它对Python解释器进行了不必要的计算。虽然目前尚不清楚这是否可以容忍,如果同时它允许不太复杂的数据库查询(难以评估的权衡),底层数据库查询并不是很简单。

第二种方法涉及通过关系查找获取额外的1:1数据,然后检查任何记录是否满足不同的1:n关系的访问标准。

我们来看看它们。

  • bt_id = user.realtor.billing_tier.id:这是获取以下1:n查询的钩子所必需的。它本身确实非常低效。它可以通过两种方式进行优化。
    • 根据Django: Access Foreign Keys Directly,可以将其写为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之间的数据流量。
  • 甚至可以使用Django的prefetch_related将1:1和1:n查询合并到一个查询中,但判断是否支付更难。值得一试。

在任何情况下,都值得安装一个名为Django Debug Toolbar的gem,它允许您分析实现在数据库查询上花费的时间。