django子查询过滤器使用paret查询中的字段值

时间:2016-09-16 04:20:30

标签: django django-queryset django-filter

我有以下模特:

class Domain(models.Model):
    name = models.CharField(...)
    plan = models.ForeignKey(Plan, ....)

class Plan(models.Model):
    name = models.CharField(...)
    num_ex_accounts = models.IntegerField(...)

class PlanDetails(models.Model):
    accounts = models.IntegerField()
    plan = models.ForeignKey(Plan, ....)

class Mailbox(models.Model):
    domain = models.ForeignKey(Domain, ...)

任何域都有一个计划,任何计划都有N个计划详细信息,其中包含使用域创建邮箱的帐户值,我想进入一个超过帐户值的查询集域,使用原始sql,sql就像:

SELECT domain
FROM domain, plan 
WHERE plan.id = domain.plan_id 
  AND (
    SELECT SUM(accounts) 
    FROM plandetails WHERE plandetails.plan_id=plan.id
  ) 
  <= 
  (
    SELECT COUNT(*) 
    FROM mailbox WHERE mailbox.domain_id=domain.id
  )

我在django中试过这样的事情:

domains = Domain.objects.filter(
               Q(
                   PlainDetails.objects.filter(plan = Domain.plan).aggregate(Sum('accounts'))
                   <=
                   Mailbox.objects.filter(domain = Domain).count()
               )
           )

但是不起作用,它会抛出关于Domain.plan的错误,有没有办法在子查询中从父查询引用该字段值?这个查询集有效还是有另一个(更好的)方法?或者我应该只使用原始sql,在这种情况下最好的选择是什么?

1 个答案:

答案 0 :(得分:1)

如果您使用的是Django 1.8及更高版本,请尝试以下操作:

Domain.objects.annotate(
    account_sum=Sum('plan__plandetails_set__accounts'),
    mailbox_count=Count('mailbox_set__id'))
).filter(
    account_sum__lte=F('mailbox_count')
)

如果您在plandetails_set字段中明确指定任何mailbox_set,则将related nameForeignKey替换为相应的Plan。如果没有指定,那么这两个只是默认值。

<强>更新 对于非常复杂的查询,使用游标运行实际SQL可能更好。 Django ORM通常足够好,但根据需要,它可能还不够。请查看docs