使用多对多成员的属性查询对象

Querying objects using attribute of member of many-to-many

我有以下型号:

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

成员基本上只是指向功能的指针。所以假设我有特点:

那么成员将是:

我想从 "ok members." 的列表中找到包含一个或多个成员的所有特征 目前我的查询如下所示:

# 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 语句进行调试,只是为了确保实际解析了任何内容,我得到以下信息:

print(ndtmp.count())
>>> 12747
print(len(sids))
>>> 12747
print(okmems.count())

... 然后代码只挂了几分钟,最后我退出了它。我认为我只是使查询过于复杂,但我不确定如何最好地简化它。我应该:

  1. 迁移功能以使用 CharField 而不是 BigIntegerField?我没有真正的理由使用 BigIntegerField,我这样做只是因为我在开始这个项目时正在学习教程。我尝试了一个简单的迁移,只是在 models.py 中更改它,我在 PostgreSQL 的列中得到了一个 "numeric" 值,格式为 'Decimal:( the id )',但可能有一些方法可以强制它只需将 id 推入字符串即可。

  2. 使用我不知道的多对多字段的一些功能来更有效地检查匹配项

  3. 计算每个特征的边界框并将其存储在另一列中,这样我就不必每次查询数据库时都进行此计算(因此只需计算一次固定成本迁移 + 每次添加新功能或修改现有功能时的计算成本)?

还是别的?如果有帮助,这是我正在进行的 OpenStreetMap 相关项目的服务器端脚本,您可以看到正在进行的工作 here

编辑 - 我认为获得 ndids 的更快方法是这样的:

ndids = ndtmp.values_list('feature_id', flat=True)

这行得通,生成了一组非空的 ID。 不幸的是,我仍然不知道如何获得 okmems。我试过了:

okmems = Member.objects.filter(ref__in=str(ndids))

但它 returns 是一个空查询集。我可以通过以下测试确认参考点是否正确:

Member.objects.values('ref')[:1]
>>> [{'ref': '2286047272'}]
Feature.objects.filter(feature_id='2286047272').values('feature_id')[:1]
>>> [{'feature_id': '2286047272'}]

你应该看看 annotate:

okmems = Member.objects.annotate(
    feat_count=models.Count('feature')).filter(feat_count__gte=1)
relsways = Feature.geoobjects.filter(members__in=okmems)

最终,我错误地在一个 table 中使用数字 ID 并在另一个中使用文本类型 ID 来设置数据库。我对迁移还不是很熟悉,但从某种意义上说,我将不得不深入研究这个世界并弄清楚如何迁移我的数据库以在两者上使用数字。现在,这有效:

# 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!

基本上,我正在制作一个 feature_ids(数字)查询集,并将其转换为文本类型 (varchar) 字段值的查询集。然后我使用 values_list 使其仅包含这些字符串 id 值,然后我找到其 ref id 在该允许功能列表中的所有成员。现在我知道哪些成员是允许的,所以我可以过滤掉所有包含该允许列表中的一个或多个成员的功能。最后,我将这个包含成员的允许特征的查询集与 ndtmp,我不包含成员的允许特征的原始查询集组合起来。