1

我有一个Realtor带有 ForeignKey 字段 ( ) 的模型 ( 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来自 queryset 的 ManyToMany 字段值)的解决方案,但我同样不确定是否对每个页面加载使用查询。我必须从房地产经纪人那里获取计费层 ID:bt_id = user.realtor.billing_tier.id然后像这样查询模型:

BillingTier.objects.filter(id = bt_id).filter(plans__automatic_feedback=True).distinct()

我认为第二个选项读起来更好,但我认为第一个会执行得更好,因为我不必导入和查询 BillingTier 模型。

有没有更好的选择,或者这两个是我所希望的最好的选择?此外,对于每个页面加载,哪个更有效?

4

1 回答 1

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,我们就节省了一个额外的 DB 命中。
  • BillingTier.objects.filter(id = bt_id).filter(plans__automatic_feedback=True).distinct()可以使用 Django 的存在进行优化,因为这将减少数据库中的工作以及数据库和 Python 之间的数据流量。
  • 也许甚至prefetch_related可以使用 Django 将 1:1 和 1:n 查询组合成一个查询,但要判断这是否有效则要困难得多。可能值得一试。

无论如何,值得安装一个名为Django Debug Toolbar的 gem ,它可以让你分析你的实现在数据库查询上花费了多少时间。

于 2013-04-30T06:41:22.957 回答