Python Django:加入同一个表

Vit*_*t D 6 python django postgresql

我正在尝试ltree在PostgreSQL中使用扩展来构建全文地址搜索引擎.

我的模型看起来像这样(它略微简化):

from django.db import models


class Addresses(models.Model):
    name = models.CharField(max_length=255)
    path = models.CharField(max_length=255)
Run Code Online (Sandbox Code Playgroud)

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

id  |   name       |  path
----------------------------
 1  |  USA         | 1
 2  |  California  | 1.2
 3  |  Los Angeles | 1.2.3
Run Code Online (Sandbox Code Playgroud)

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

    id  |           full_name            |  path
-------------------------------------------------
  1     |  USA                           |   1
  2     |  California USA                |   1.2
  3     |  Los Angeles California USA    |   1.2.3
Run Code Online (Sandbox Code Playgroud)

我这样做,所以用户可以执行像'los ang cali'或类似的查询.使用原始 PostgreSQL查询我没有问题:

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;
Run Code Online (Sandbox Code Playgroud)

这工作正常,但在使用RawQuerySet,我不能用之类的东西.filter(),.group_by(),分页等.

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

JOIN "Addresses" AS a ON (a.path @> s.path)
Run Code Online (Sandbox Code Playgroud)

它是用来连接各元素的所有祖先,然后用它们聚集array_agg(),array_to_string功能,因此这些功能的输出可进一步使用全文搜索.

如果有人有更好的想法如何使用Django ORM实现这种事情,请指教.

e4c*_*4c5 2

概括

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

非托管模型。

通过将模型的托管元选项设置为 false 来创建非托管模型。

如果为 False,则不会对该模型执行任何数据库表创建或删除操作。如果模型表示现有表或通过其他方式创建的数据库视图,这非常有用。这是 Managed=False 时的唯一区别。模型处理的所有其他方面与正常情况完全相同。这包括

强调我的。

因此,如果您创建一个非托管模型,它可以由数据库上的视图表示,并且您可以访问.filter().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
Run Code Online (Sandbox Code Playgroud)

创建模型

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
Run Code Online (Sandbox Code Playgroud)

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

迁徙。

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

要修复此问题并自动创建视图,请operations向该迁移中的列表添加 RunSQL 调用。

migrations.RunSQL(''' COPY PASTE SQL QUERY FROM ABOVE ''')
Run Code Online (Sandbox Code Playgroud)

注意事项

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