Django queryset-添加约束

Django queryset-添加约束,django,group-by,sum,django-queryset,having,Django,Group By,Sum,Django Queryset,Having,我已经使用Django好几年了,但今天我正在努力为组添加约束 我的查询集如下所示: crm_models.Contact.objects\ .filter(dealercontact__dealer__pk__in=(265,), dealercontact__activity='gardening', date_data_collected__gte=datetime.date(2012,10,1), date_data_collected

我已经使用Django好几年了,但今天我正在努力为
组添加
约束

我的查询集如下所示:

crm_models.Contact.objects\
.filter(dealercontact__dealer__pk__in=(265,),
         dealercontact__activity='gardening',
         date_data_collected__gte=datetime.date(2012,10,1),
         date_data_collected__lt=datetime.date(2013,10,1))\
.annotate(nb_rels=Count('dealercontact'))
这给了我以下MySQL查询:

SELECT *
FROM `contact` 
LEFT OUTER JOIN `dealer_contact` ON (`contact`.`id_contact` = `dealer_contact`.`id_contact`) 
WHERE (`dealer_contact`.`active` = True 
   AND `dealer_contact`.`activity` = 'gardening'  
   AND `contact`.`date_data_collected` >= '2012-10-01'  
   AND `contact`.`date_data_collected` < '2013-10-01'
   AND `dealer_contact`.`id_dealer` IN (265)) 
GROUP BY `contact`.`id_contact`
ORDER BY NULL;
我如何用Django Queryset修复这个问题?在这种情况下,我需要一个查询集

在这里,我使用annotate只是为了在
contact.id\u contact
上获取
groupby

编辑:我的目标是获得在dealercontact中没有“客户”关系但有“参考”关系的联系人(当然根据WHERE子句)

模型 我的目标是让那些与公司没有“客户”关系的联系人参与进来 dealercontact但具有“参考”关系(根据WHERE 条款(当然)

此简单查询满足此要求:

Contact.objects.filter(dealercontact__type="ref").exclude(dealercontact__type="customer")
这足够了吗,还是你需要它来做更多的事情

更新:如果您的要求是

具有“参考”关系但没有“客户”关系的联系人 与同一经销商的关系

您可以这样做:

from django.db.models import Q
Contact.objects.filter(Q(dealercontact__type="ref") & ~Q(dealercontact__type="customer"))

您是否仅仅因为需要orm对象而使用原始查询?使用
Contact.objects.raw()
生成类似的
实例过滤器
。请参阅以获取更多帮助。

我通过在
DealerContact
中添加两个二进制字段解决了这个问题:
是ref
是customer

如果
type='ref'
为\u ref=1
为\u customer=0
。 否则,如果
type='customer'
为\u ref=0
为\u customer=1

因此,我现在可以使用
注释(nb_customers=Sum('is_customer'))
然后使用
过滤器(nb_customers=0)

最终查询集包括:

Contact.objects.filter(dealercontact__dealer__pk__in=(265,),  
                       dealercontact__activity='gardening', 
                       date_data_collected__gte=datetime.date(2012,10,1),
                       date_data_collected__lt=datetime.date(2013,10,1))\
               .annotate(nb_customers=Sum('dealercontact__is_customer'))\
               .filter(nb_customers=0)

实际上,如果需要,可以添加自己的自定义
HAVING
groupby
子句

请谨慎使用我的示例-如果Django ORM代码/路径在未来的Django版本中发生更改,您也必须更新代码

图像您有图书和版本模型,其中每本书可以有多个版本,并且您希望在图书查询集中选择第一个美国版本日期

在Django 1.5+中添加具有
分组依据子句的自定义

from django.db.models import Min
from django.db.models.sql.where import ExtraWhere, AND

qs = Book.objects.all()

# Standard annotate
qs = qs.annotate(first_edition_date=Min("edition__date"))

# Custom HAVING clause, to limit annotation by US country only
qs.query.having.add(ExtraWhere(['"app_edition"."country"=%s'], ["US"]), AND)

# Custom GROUP BY clause will be needed too
qs.query.group_by.append(("app_edition", "country"))

ExtraWhere
不仅可以包含字段,还可以包含任何原始sql条件和函数。

我不确定这一点,但可能.extra()在这里很有用,我尝试过,但注释+extra给了我以下错误:DatabaseError:(1111,“组函数的使用无效”)。查询集将“SUM(…)”添加到SELECT子句和GROUPBY子句中,我对此感到奇怪。也许我可以在没有注释的情况下使用extra,并且仍然保留get GROUP BY?当您使用Django ORM时,您不应该考虑SQL语句。这并不总是可能的,因此
.extra()
.raw()
,但是如果你明确地满足了你想要的效果,那么给你的问题一个好的答案就会容易得多。你能添加你的模型吗?我不尝试用SQL来思考,但我正在尝试适应Django Querysets的一个弱点,但我希望我能简单地做到,我错了。我首先从一个简单的Django Queryset中获得了上面的MySQL查询。我编辑了我的问题:我的目标是获得在dealercontact中没有“customer”关系但有“ref”关系的联系人。谢谢:)使用
.raw()
应该是最后的选择。它首先违背了使用ORM的目的。不幸的是,没有。Django在排除时似乎没有考虑过滤器参数,因此排除了与其他经销商(例如与其他经销商有dealercontact type='customer'的联系人)。我在这里得不到我需要的东西。我很难理解你的要求。这正是我理解他们的方式。这将返回与任何人都没有单一“客户”关系的联系人,以及与某人至少有一个“参考”关系的联系人。如果这不是您想要的,请澄清。是“与同一经销商有“参考”关系但没有“客户”关系的联系人”吗?是的,事实上,对于过滤器中给出的其他参数,没有与给定经销商的客户关系。恐怕我仍然无法理解您的目标,所以我帮不了你。抱歉。它只是给出了一个错误:“Query”对象没有“having”属性
Contact.objects.filter(dealercontact__dealer__pk__in=(265,),  
                       dealercontact__activity='gardening', 
                       date_data_collected__gte=datetime.date(2012,10,1),
                       date_data_collected__lt=datetime.date(2013,10,1))\
               .annotate(nb_customers=Sum('dealercontact__is_customer'))\
               .filter(nb_customers=0)
from django.db.models import Min
from django.db.models.sql.where import ExtraWhere, AND

qs = Book.objects.all()

# Standard annotate
qs = qs.annotate(first_edition_date=Min("edition__date"))

# Custom HAVING clause, to limit annotation by US country only
qs.query.having.add(ExtraWhere(['"app_edition"."country"=%s'], ["US"]), AND)

# Custom GROUP BY clause will be needed too
qs.query.group_by.append(("app_edition", "country"))