简化设置
class System(models.Model):
fields...
vulnerabilities = models.ManyToManyField('Vulnerability', through='SystemVulnerability')
class Vulnerablity(models.Model):
name = ...
risk = CharField(choices=['H','M','L','I'])
...
class SystemVulnerability(models.Model):
vulnerability = ForeignKey
system = ForeignKey
fields...
sv_list = [199026, 199036, 199046, 199048, ....]
other_filter = Q(... lots of stuff...) #not including this for brevity
System.objects.filter(other_filter & Q(systemvulnerability__in=sv_list)).query.__str__()
u'SELECT "system"."id", "system"."ip", "system"."org_id", "system"."dnsname", "system"."ipownercode", "system"."securityplan"
FROM "system"
INNER JOIN "systemvulnerability" ON ( "system"."id" = "systemvulnerability"."system_id" )
WHERE "systemvulnerability"."id" IN (199026, 199036, 199046, 199048, ....)'
这看起来不错。如果我们否定它呢?
System.objects.filter(other_filter & ~Q(systemvulnerability__in=sv_list)).query.__str__()
u'SELECT "system"."id", "system"."ip", "system"."org_id", "system"."dnsname", "system"."ipownercode", "system"."securityplan"
FROM "system"
WHERE NOT ("system"."id" IN (SELECT U1."system_id" FROM "systemvulnerability" U1 WHERE U1."id" IN (199026, 199036, 199046, 199048, ....)))'
什么?我希望得到的查询是:
u'SELECT "system"."id", "system"."ip", "system"."org_id", "system"."dnsname", "system"."ipownercode", "system"."securityplan"
FROM "system"
INNER JOIN "systemvulnerability" ON ( "system"."id" = "systemvulnerability"."system_id" )
WHERE "systemvulnerability"."id" NOT IN (199026, 199036, 199046, 199048, ....)'
如何修改我的语句,使结果查询更像我的预期?
Example data
System:
s1
s2
s3
Vulnerability:
v1
v2
v3
SystemVulnerability:
sv1 = s1, v1
sv2 = s1, v2
sv3 = s2, v2
sv4 = s3, v2
sv5 = s3, v3
System.filter(Q(systemvulnerability__in=[sv1, sv4]))
Get back s1, s3: Good!
System.filter(~Q(systemvulnerability__in=[sv1, sv2]))
Get back: s2, s3: Good!
System.filter(~Q(systemvulnerability__in=[sv1, sv4, sv5]))
Get back s2: Bad!
But I wanted s1, s2
显然我可以基于 SystemVulnerability 进行查询,但我的过滤器实际上比这复杂得多,所以我想保持它基于 System.Vulnerability。
我想 aSystem.objects.filter(pk__in=SystemVulnerability.filter(~Q(systemvulnerability__in=sv_list)).values_list('system'))
可以工作,但在 SQL 中生成的 IN 子句相当大而且速度很慢,尤其是随着我的数据增长。
编辑:我去尝试用前一行的 IN 子句来做。它有效,但最终否定了我的许多其他过滤器。我真的只需要 ~Q() 才能正常工作!