Querying objects using attribute of member of many-to-many
我有以下型号:
1 2 3 4 5 6 7 8 9 10 | class Member(models.Model): ref = models.CharField(max_length=200) # some other stuff def __str__(self): return self.ref class Feature(models.Model): feature_id = models.BigIntegerField(default=0) members = models.ManyToManyField(Member) # some other stuff |
成员基本上只是指向某个特性的指针。所以假设我有以下特点:
- 特征_id=2,成员=1,2
- StuturyID=4
- StuturyID=3
那么成员将是:
- ID=1,REF=4
- ID=2,REF=3
我想从"确定成员"列表中查找包含一个或多个成员的所有功能。当前我的查询如下所示:
1 2 3 4 5 6 7 | # ndtmp is a query set of member-less Features which Members can point to sids = [str(i) for i in list(ndtmp.values('feature_id'))] # now make a query set that contains all rels and ways with at least one member with an id in sids okmems = Member.objects.filter(ref__in=sids) relsways = Feature.geoobjects.filter(members__in=okmems) # now combine with nodes op = relsways | ndtmp |
这速度太慢了,我甚至不确定它是否起作用。我尝试使用print语句进行调试,只是为了确保实际正在分析任何内容,我得到了以下信息:
1 2 3 4 5 | print(ndtmp.count()) >>> 12747 print(len(sids)) >>> 12747 print(okmems.count()) |
…然后代码就挂了几分钟,最后我放弃了它。我认为我只是过度简化了这个查询,但我不确定如何最好地简化它。我应该:
迁移功能以使用charfield而不是bigintegerfield?我没有真正的理由使用bigintegerfield,我之所以这样做是因为我开始这个项目时遵循了一个教程。我尝试了一个简单的迁移,只是在models.py中对其进行了更改,在postgresql的列中得到了一个格式为"decimal:(the id)"的"numeric"值,但可能有某种方法可以强制它将ID推入字符串。
使用一些我不知道的多对多字段的特性来更有效地检查匹配
计算每个特征的边界框,并将其存储在另一列中,这样我就不必每次查询数据库时都进行计算(因此,只需迁移时的单一固定计算成本+每次添加新特征或修改现有特征时的计算成本)?
或者别的什么?如果有帮助的话,这是我正在进行的OpenStreetmap相关项目的服务器端脚本,您可以在这里看到正在进行的工作。
编辑-我认为获得
1 | ndids = ndtmp.values_list('feature_id', flat=True) |
这样可以产生一组非空的ID。不幸的是,我对如何得到Okmems仍然一无所知。我试过:
1 | okmems = Member.objects.filter(ref__in=str(ndids)) |
但它返回一个空查询集。我可以通过以下测试确认参考点是正确的:
1 2 3 4 | Member.objects.values('ref')[:1] >>> [{'ref': '2286047272'}] Feature.objects.filter(feature_id='2286047272').values('feature_id')[:1] >>> [{'feature_id': '2286047272'}] |
最后,我在一个表中使用数字ID,在另一个表中使用文本类型ID来设置数据库是错误的。我对迁移还不是很熟悉,但在某种程度上,我必须深入研究这个世界,并找出如何迁移数据库,以便在两者上都使用数字。目前,这是可行的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # ndtmp is a query set of member-less Features which Members can point to # get the unique ids from ndtmp as strings strids = ndtmp.extra({'feature_id_str':"CAST( \ feature_id AS VARCHAR)"}).order_by( \ '-feature_id_str').values_list('feature_id_str',flat=True).distinct() # find all members whose ref values can be found in stride okmems = Member.objects.filter(ref__in=strids) # find all features containing one or more members in the accepted members list relsways = Feature.geoobjects.filter(members__in=okmems) # combine that with my existing list of allowed member-less features op = relsways | ndtmp # prove that this set is not empty op.count() # takes about 10 seconds >>> 8997148 # looks like it worked! |
基本上,我正在创建一个由
你应该看看
1 2 3 | okmems = Member.objects.annotate( feat_count=models.Count('feature')).filter(feat_count__gte=1) relsways = Feature.geoobjects.filter(members__in=okmems) |