如何在 Django 中创建/使用自定义数据库函数

2024-06-28

序幕:

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

  • Django GEOS 中 PostGIS ST_MakeValid 的等效项 https://stackoverflow.com/questions/45631855/equivalent-of-postgis-st-makevalid-in-django-geos/
  • Geodjango:如何从点缓冲 https://stackoverflow.com/questions/19144834/geodjango-how-to-buffer-from-point/45879956#45879956
  • 从 django PolygonField 获取随机点 https://stackoverflow.com/questions/46455081/get-random-point-from-django-polygonfield/46464362#46464362
  • Django自定义复杂Func(sql函数) https://stackoverflow.com/questions/46427115/django-custom-for-complex-func-sql-function/46490971#46490971

并可应用于上述以及以下情况:

  • 日期时间对象上的 Django F 表达式 https://stackoverflow.com/questions/43890131/django-f-expression-on-datetime-objects/43890979#43890979

我想在 SO 文档上编写一个示例,但由于它已于 2017 年 8 月 8 日关闭,我将遵循以下建议这个被广泛投票和讨论的元答案 https://meta.stackoverflow.com/questions/354217/sunsetting-documentation/354302#354302并将我的示例写为自我回答的帖子。

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


问题:

Django/GeoDjango 有一些数据库功能,例如Lower() https://docs.djangoproject.com/en/1.11/ref/models/database-functions/#lower or MakeValid() https://docs.djangoproject.com/en/1.11/ref/contrib/gis/functions/#makevalid可以这样使用:

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

有没有办法根据现有数据库函数使用和/或创建我自己的自定义数据库函数,例如:

  • Position() https://www.w3schools.com/sql/func_mysql_position.asp (MySQL)
  • TRIM() http://www.sqlitetutorial.net/sqlite-functions/sqlite-trim/(SQLite)
  • ST_MakePoint() https://postgis.net/docs/ST_MakePoint.html(PostgreSQL 与 PostGIS)

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


Django 提供了Func() https://docs.djangoproject.com/en/1.11/ref/models/expressions/#func-expressions表达式以方便在查询集中调用数据库函数:

Func()表达式是涉及数据库函数的所有表达式的基本类型,例如COALESCE and LOWER,或像这样的聚合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. Use Func() https://docs.djangoproject.com/en/1.11/ref/models/expressions/#func-expressions直接调用该函数:

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

    • 聚合 https://docs.djangoproject.com/en/1.11/topics/db/aggregation/为数据库中的每个条目添加一个字段。
    • F() https://docs.djangoproject.com/en/1.11/ref/models/expressions/#f-expressions这使得在模型字段上以及模型字段之间执行算术运算。 https://stackoverflow.com/questions/45593440/how-to-execute-arithmetic-operations-between-model-fields-in-django
    • Value() https://github.com/django/django/blob/master/django/db/models/expressions.py#L620这将清理任何给定值(为什么这很重要?) https://stackoverflow.com/questions/332365/how-does-the-sql-injection-from-the-bobby-tables-xkcd-comic-work?answertab=votes#tab-top

    查询:

    MyModel.objects.aggregate(
        pos=Func(F('name'), Value(subst), function='POSITION')
    )
    
  2. 创建您自己的数据库功能扩展Func https://docs.djangoproject.com/en/1.11/ref/models/expressions/#func-expressions:

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

    class Position(Func):
        function = 'POSITION'
    

    并在查询中使用它:

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

GeoDjango 附录:

In 地理Django https://docs.djangoproject.com/en/1.11/ref/contrib/gis/,为了导入 GIS 相关函数(例如PostGIS's Transform函数)的Func() https://docs.djangoproject.com/en/1.11/ref/models/expressions/#func-expressions方法必须替换为GeoFunc() https://github.com/django/django/blob/master/django/contrib/gis/db/models/functions.py#L88,但本质上是在相同的原则下使用的:

class Transform(GeoFunc):
    function='ST_Transform'

还有更复杂的情况GeoFunc这里出现了用法和一个有趣的用例:如何在 Django 中计算 Frechet 距离? https://stackoverflow.com/questions/56475858/how-to-calculate-frechet-distance-in-django/56483387#56483387


泛化自定义数据库函数附录:

如果您想要创建自定义数据库函数(选项 2)并且希望能够在事先不知道的情况下将其与任何数据库一起使用,您可以使用Func's 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')
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 Django 中创建/使用自定义数据库函数 的相关文章

随机推荐