Python Django:加入同一个表

时间:2015-12-30 16:41:56

标签: python django postgresql

我试图在PostgreSQL中使用{"Key":{"key":"someNumber"},"keys":[{"key":"10"},{"key":"11"},{"key":"12"},{"key":"13"},{"key":"14"}]} ####### someNumber 10 11 12 13 14 扩展来构建全文地址搜索引擎。

我的模型看起来像这样(它稍微简化了一下):

ltree

因此,此表中的数据将如下所示:

from django.db import models


class Addresses(models.Model):
    name = models.CharField(max_length=255)
    path = models.CharField(max_length=255)

我想对每个实体的聚合名称进行全文搜索。 基本上我需要将表中的每一行转换为下一个格式来进行搜索:

id  |   name       |  path
----------------------------
 1  |  USA         | 1
 2  |  California  | 1.2
 3  |  Los Angeles | 1.2.3

我以这种方式做到这一点,因此用户可以执行像“洛杉矶”这样的查询。或类似的。 使用原始 PostgreSQL查询我没有问题:

    id  |           full_name            |  path
-------------------------------------------------
  1     |  USA                           |   1
  2     |  California USA                |   1.2
  3     |  Los Angeles California USA    |   1.2.3

效果很好,但在使用 RawQuerySet 时,我无法使用SELECT *, ts_rank_cd(to_tsvector('english', full_address), query) AS rank FROM (SELECT s.id, s.path, array_to_string(array_agg(a.name ORDER BY a.path DESC), ' ') AS full_address FROM "Addresses" AS s INNER JOIN "Addresses" AS a ON (a.path @> s.path) GROUP BY s.id, s.path, s.name ) AS subquery, to_tsquery('english', %s) as query WHERE to_tsvector('english', full_address) @@ query ORDER BY rank DESC; .filter(),分页等内容。

在Django中重现它的主要限制是 JOIN

.group_by()

它用于连接每个元素的所有祖先,然后使用JOIN "Addresses" AS a ON (a.path @> s.path) array_agg()函数聚合它们,因此这些函数的输出可以在 full-中进一步使用文字搜索

如果有人有更好的想法如何使用 Django ORM 来实现这类事情,请提供建议。

2 个答案:

答案 0 :(得分:2)

摘要

您需要一个由VIEW支持的非托管模型。

非托管模型。

通过将模型的managed元选项设置为false,可以创建非托管模型。

  

如果为False,则不会创建或删除数据库表操作   为这个模型执行。如果模型代表一个,这很有用   现有表格或其他人创建的数据库视图   手段。当managed = False时,这是唯一的区别。所有其他   模型处理的各个方面与正常情况完全相同。这个   包括

强调我的。

因此,如果您创建一个非托管模型,它可以由数据库上的视图表示,并且您可以访问.group_by()CREATE OR REPLACE view full_address_tree AS SELECT a.*, s.id, s.path, array_to_string(array_agg(a.name ORDER BY a.path DESC), ' ') AS full_address FROM "Addresses" AS s INNER JOIN "Addresses" AS a ON (a.path @> s.path) GROUP BY s.id, s.path, s.name

视图。

视图是您的查询。

class FullAddressTree(models.Model):
    # copy paste the fields from your Addresses model here
    sid = models.IntegerField()
    sid = models.CharField()

    class Meta:
        # this is the most important part
        managed = False
        db_table = 'full_address_tree' # the name of the view

创建模型

operations

因此,现在您有了一个可用于进行全文搜索的模型,而无需求助于原始查询。因此,您可以随意使用Django ORM的全部功能。

迁移。

如果您想要迁移,您会发​​现./manage.py makemigrations会导致虚拟迁移。 ./manage.py sqlmigrate将显示没有为此迁移执行sql查询。

要修复它并自动创建视图,请将RunSQL调用添加到该迁移中的migrations.RunSQL(''' COPY PASTE SQL QUERY FROM ABOVE ''') 列表中。

$user = $this->find($id); 
$user->passports()->delete(); 
$new[] = new UserPassport(['passport' => '123456789', 'created_by' => $user->id, 'updated_by' => $user->id]); 
$new[] = new UserPassport(['passport' => '111111111', 'created_by' => $user->id, 'updated_by' => $user->id]); 
$user->passports()->saveMany($new);

注意事项

您创建的非托管模型是只读的。尝试创建,替换,更新或删除将失败。如果您需要此功能,则需要INSTEAD触发器。

答案 1 :(得分:1)

如此大的+1 @ shang-wang对django-mptt的建议。使用它可以解决您的问题,因为MPTT中的所有树操作都是常规QuerySet,因此可以链接到annotateaggregate。我唯一不确定的是你的问题是否插入沉重。如果您只是计划将大量数据转储到表中,那么没什么大不了的。如果您要经常修改树,那么可能会出现问题。有关MPTT及其工作原理的详细说明http://www.sitepoint.com/hierarchical-data-database-2/

无论如何,你获得一个节点的所有祖先的原始问题就变成了 la_node.get_ancestors()。这解决了你提到的连接约束,它可以重新构造查询的其余部分。