【问题标题】:Querying objects using attribute of member of many-to-many使用多对多成员的属性查询对象
【发布时间】:2017-03-27 20:56:28
【问题描述】:

我有以下型号:

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

Member 基本上只是一个指向 Feature 的指针。所以假设我有特点:

  • feature_id = 2, members = 1, 2
  • feature_id = 4
  • feature_id = 3

那么成员将是:

  • id = 1,参考 = 4
  • id = 2,参考 = 3

我想从“ok 成员”列表中查找包含一个或多个成员的所有功能。目前我的查询如下所示:

# 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(ndtmp.count())
>>> 12747
print(len(sids))
>>> 12747
print(okmems.count())

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

  1. 迁移功能以使用 CharField 而不是 BigIntegerField?我没有真正的理由使用 BigIntegerField,我这样做只是因为我在开始这个项目时正在遵循一个教程。我尝试了一个简单的迁移,只需在 models.py 中更改它,我在 PostgreSQL 的列中得到了一个“数字”值,格式为“十进制:(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))

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

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

【问题讨论】:

    标签: python django postgresql many-to-many postgis


    【解决方案1】:

    你应该看看annotate

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

    【讨论】:

    • 我认为你在做某事,但“models.Count('feature')”部分对我来说没有意义。一个功能有一个多对多的“列表”,里面满是成员,而不是相反。 okmems 应该包含指向我接受的功能列表中的功能的成员(我成功创建)。
    • 对不起,如果我不清楚,我想尽可能地抽象,但显然这有点粗糙,特殊的用例。基本上,我已经检索了所有不包含任何成员的功能。我们称其为“A 组”。现在我想获得其成员至少与“Set A”重叠的所有功能。操作问题似乎是成员“ref”与功能“feature_id”的比较,但我不确定。
    • 我认为问题的根源是如何将 CharField id 与 BigInt id 的 QuerySet 进行比较。我将尝试向下 this path 看看它会导致什么。
    • 对不起,我不知何故跳过了 CharField 部分。坦率地说,这对我来说似乎是一个糟糕的数据库设计。我遇到过这样的模式,维护起来很痛苦。关于您的问题 - 您可能需要检查 Member.ref 和查询中的 id 排序 - 即使 id 相同,'1, 2, 3''2, 1, 3' 也是不同的字符串。
    • 非常正确,在两个方面。明天我可能最终会投资弄清楚如何迁移到 BigInt。
    【解决方案2】:

    最终,我在一个表中使用数字 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(我原来的不包含成员的允许功能查询集)结合起来。

    【讨论】:

    猜你喜欢
    • 2012-08-27
    • 2020-10-09
    • 1970-01-01
    • 2021-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多