如何将 Substr 与 F 表达式一起使用

How to use Substr with an F Expression

我正在尝试构建一个基于 fuzzystrmatch postgres 扩展的 Func 类 的工具包。

例如,我有这个包装器,它接受一个表达式和一个搜索词以及 returns 编辑距离:

class Levenshtein(Func):
    """This function calculates the Levenshtein distance between two strings:"""
    template = "%(function)s(%(expressions)s, '%(search_term)s')"
    function = "levenshtein"

    def __init__(self, expression, search_term, **extras):
        super(Levenshtein, self).__init__(
            expression,
            search_term=search_term,
            **extras
        ) 

这样调用,使用 F Expression:

Author.objects.annotate(lev_dist=Levenshtein(F('name'),'JRR Tolkien').filter(lev_dist__lte=2)

但是,如果此处的 'name' 字段大于 255,则会引发错误:

Both source and target can be any non-null string, with a maximum of 255 characters.

当我使用 Substr 注释时,我可以截断名称:

Author.objects.annotate(clipped_name=Substr(F('name'),1,250))

但我似乎无法弄清楚如何将该逻辑放入函数中,我将其放入 ExpressionWrapper 并根据 the docs 设置 output_field :

class Levenshtein(Func):
    """This function calculates the Levenshtein distance between two strings:"""
    template = "%(function)s(%(expressions)s, '%(search_term)s')"
    function = "levenshtein"

    def __init__(self, expression, search_term, **extras):
        super(Levenshtein, self).__init__(
            expression=ExpressionWrapper(Substr(expression, 1, 250), output_field=TextField()),
            search_term=search_term,
            **extras
        ) 

虽然文档没有说得很清楚,只是通过实验发现答案是删除 expression 的额外定义并直接传入 ExpressionWrapper 作为第一个参数:

class Levenshtein(Func):
    """This function calculates the Levenshtein distance between two strings:"""
    template = "%(function)s(%(expressions)s, '%(search_term)s')"
    function = "levenshtein"

    def __init__(self, expression, search_term, **extras):
        super(Levenshtein, self).__init__(
            ExpressionWrapper(Substr(expression, 1, 250), output_field=TextField()),
            search_term=search_term,
            **extras
        )