17

序幕:

这是 SO 中经常出现的一个问题:

并且可以应用于上述以及以下:

我想写一个关于 SO 文档的示例,但由于它于 2017 年 8 月 8 日关闭,我将遵循这个广受好评和讨论的元答案的建议,并将我的示例写为一个自我回答的帖子。

当然,我也很乐意看到任何不同的方法!


问题:

Django/GeoDjango 有一些数据库函数,例如Lower()MakeValid()可以像这样使用:

Author.objects.create(name='Margaret Smith')
author = Author.objects.annotate(name_lower=Lower('name')).get()
print(author.name_lower)

有什么方法可以使用和/或创建我自己的基于现有数据库函数的自定义数据库函数,例如:

如何在 Django/GeoDjango ORM 中应用/使用这些功能?

4

1 回答 1

17

Django 提供了Func()表达式来方便在查询集中调用数据库函数:

Func()表达式是涉及数据库函数(如COALESCELOWER)或聚合(如SUM )的所有表达式的基本类型。

关于如何在 Django/GeoDjango ORM 中使用数据库函数有 2 个选项:

为方便起见,让我们假设模型已命名MyModel,并且子字符串存储在名为 的变量中subst

from django.contrib.gis.db import models as gis_models

class MyModel(models.Model):
    name = models.CharField()
    the_geom = gis_models.PolygonField()
  1. 用于Func()直接调用函数:

    我们还需要以下内容才能使我们的查询正常工作:

    查询:

    MyModel.objects.aggregate(
        pos=Func(F('name'), Value(subst), function='POSITION')
    )
    
  2. 创建您自己的数据库函数扩展Func

    我们可以扩展Func类来创建我们自己的数据库函数:

    class Position(Func):
        function = 'POSITION'
    

    并在查询中使用它:

    MyModel.objects.aggregate(pos=Position('name', Value(subst)))
    

GeoDjango 附录:

GeoDjango中,为了导入与 GIS 相关的函数(如PostGIS'sTransform函数),该Func()方法必须替换为GeoFunc(),但它本质上是在相同的原则下使用的:

class Transform(GeoFunc):
    function='ST_Transform'

有更复杂的GeoFunc使用案例,这里出现了一个有趣的用例:如何在 Django 中计算 Frechet 距离?


泛化自定义数据库功能附录:

如果您想创建自定义数据库函数(选项 2),并且希望能够在事先不知道的情况下将其与任何数据库一起使用,则可以使用Func'as_<database-name>方法,前提是您要使用的函数存在于每个数据库

class Position(Func):
    function = 'POSITION' # MySQL method

    def as_sqlite(self, compiler, connection):
        #SQLite method
        return self.as_sql(compiler, connection, function='INSTR')

    def as_postgresql(self, compiler, connection):
        # PostgreSQL method
        return self.as_sql(compiler, connection, function='STRPOS')
于 2017-10-11T13:51:05.873 回答