• en
  • Language: ru
  • Documentation version: 2.0

17. Как использовать произвольные функции базы данных в кверисетах?

Django поставляется с такими функциями, как Lower, Coalesce и Max, но он не может поддерживать все функции базы данных, особенно те, которые специфичны для базы данных.

Django предоставляет Func, что позволяет использовать произвольные функции базы данных, даже если Django их не предоставляет.

Postgres имеет расширение fuzzystrmatch, которое предоставляет несколько функций для определения сходства. Установите расширение в вашу БД postgres с помощью create extension fuzzystrmatch.

Мы будем использовать функцию levenshtein. Сначала создадим несколько объектов Hero.

Hero.objects.create(name="Zeus", description="A greek God", benevolence_factor=80, category_id=12, origin_id=1)
Hero.objects.create(name="ZeuX", description="A greek God", benevolence_factor=80, category_id=12, origin_id=1)
Hero.objects.create(name="Xeus", description="A greek God", benevolence_factor=80, category_id=12, origin_id=1)
Hero.objects.create(name="Poseidon", description="A greek God", benevolence_factor=80, category_id=12, origin_id=1)

Мы хотим найти Hero объектов, которые имеют name сходство с Зевсом. Вы можете сделать

from django.db.models import Func, F
Hero.objects.annotate(like_zeus=Func(F('name'), function='levenshtein', template="%(function)s(%(expressions)s, 'Zeus')"))

like_zeus=Func(F('name'), function='levenshtein', template="%(function)s(%(expressions)s, 'Zeus')") принимала два аргумента, которые позволяли представить базу данных, а именно function и template. Если вам нужно повторно использовать функцию, вы можете определить класс следующим образом.

class LevenshteinLikeZeus(Func):
    function='levenshtein'
    template="%(function)s(%(expressions)s, 'Zeus')"

А затем используйте Hero.objects.annotate(like_zeus=LevenshteinLikeZeus(F("name")))

Затем вы можете фильтровать по этому аннотированному полю следующим образом.

In [16]: Hero.objects.annotate(
    ...:         like_zeus=LevenshteinLikeZeus(F("name"))
    ...:     ).filter(
    ...:         like_zeus__lt=2
    ...:     )
    ...:
Out[16]: <QuerySet [<Hero: Zeus>, <Hero: ZeuX>, <Hero: Xeus>]>