如何在 Django 中制作/使用自定义数据库函数

Posted

技术标签:

【中文标题】如何在 Django 中制作/使用自定义数据库函数【英文标题】:How to make/use a custom database function in Django 【发布时间】:2018-03-23 05:35:01 【问题描述】:

序幕:

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

Equivalent of PostGIS ST_MakeValid in Django GEOS Geodjango: How to Buffer From Point Get random point from django PolygonField Django custom for complex Func (sql function)

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

Django F expression on datetime objects

我想写一个关于 SO Documentation 的示例,但由于它已于 2017 年 8 月 8 日关闭,我将按照 this widely upvoted and discussed meta answer 的建议将我的示例写为一个自我回答的帖子。

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


问题:

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

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

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

Position() (mysql) TRIM() (SQLite) ST_MakePoint()(带有 PostGIS 的 PostgreSQL)

如何在 Django/GeoDjango ORM 中应用/使用这些函数?

【问题讨论】:

【参考方案1】:

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()

    使用Func()直接调用函数:

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

    Aggregation 为我们数据库中的每个条目添加一个字段。 F() 允许 the execution of arithmetic operations on and between model fields. Value() 将清理任何给定值 (why is this important?)

    查询:

    MyModel.objects.aggregate(
        pos=Func(F('name'), Value(subst), function='POSITION')
    )
    

    创建您自己的扩展Func的数据库函数:

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

    class Position(Func):
        function = 'POSITION'
    

    并在查询中使用它:

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

GeoDjango 附录:

在GeoDjango中,为了导入一个GIS相关函数(如PostGISTransform函数),Func()方法必须替换为GeoFunc(),但本质上是在相同的原理下使用:

class Transform(GeoFunc):
    function='ST_Transform'

GeoFunc 的使用情况更复杂,这里出现了一个有趣的用例:How to calculate Frechet Distance in Django?


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

如果您想创建自定义数据库函数(选项 2),并且希望能够在事先不知道的情况下将其与任何数据库一起使用,您可以使用 Funcas_<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')

【讨论】:

你能帮我处理一下this question吗?我很难从 Django 调用 ST_FrechetDistance 函数。 我喜欢这个 1 人工 QA。我唯一的批评是 GeoDjango 附录除了最初的混乱之外什么也没增加。从GeoFunc 的精美链接源代码来看,FuncGeoFunc 之间似乎没有区别。如果是这种情况,我认为删除 GeoDjano 附录部分会改善答案,因为它只会让 GeoDjango 用户(至少是我)相信这两个类之间存在差异。既然不是这样,为什么要包括附录?...如果我误解了FuncGeoFunc之间的区别,请指教。干杯,谢谢! @vitale232 虽然FuncGeoFunc 看起来很相似,但它们本质上是不同的。 Func 无法处理以 PointsPolygons 等为参数的地理函数(如果您愿意,可以在测试项目中对其进行测试)。 GeoFunc 处理函数的方式略有不同,可以在此处看到:github.com/django/django/blob/…(将GeoFuncFunc 分开的是GeoFuncMixin)。也感谢您对我的 QA 的认可:D 谢谢,@JohnMoutafis。我在浏览源代码时变得懒惰,未能找到GeoFuncMixin 源代码。这样就解决了:)

以上是关于如何在 Django 中制作/使用自定义数据库函数的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Django 自定义数据库函数调用周围避免 SQL 中的括号?

django中如何设置函数自定义执行

Django 模板之自定义函数

Django自定义模板函数

如何在 IOS 上制作可自定义的视图? (我使用 style.id 作为 android 视图类构造函数的参数)

如何使 Vue.js 将散列密码发布到 Django REST API AbstractBaseUser 自定义模型?