SQLAlchemy中的数据库函数命名参数

1 投票
1 回答
2555 浏览
提问于 2025-04-17 18:24

我在我的数据库(Postgres)里有一个函数,长得像这样:

create function test_f(a text default '*', b text default '+') returns text as $$
    select a || ' ' || b;
$$ language sql;

Postgres 允许用命名参数来调用这个函数:

mytest=> select test_f('a', 'b');                                           
test_f                                                                      
--------                                                                    
a b                                                                         
(1 row)                                                                     

mytest=> select test_f('a');                                                
test_f                                                                      
--------                                                                    
a +                                                                         
(1 row)                                                                     

mytest=> select test_f(b:='a');                                             
test_f                                                                      
--------                                                                    
* a                                                                         
(1 row)                                                                     

我想在 Python 中也这样做,使用 SQLAlchemy 的 func 结构,但是看起来 func 不支持命名参数:

In [85]: print(sqlalchemy.func.test_f('a', 'b'))                            
test_f(:test_f_1, :test_f_2)                                                

In [86]: print(sqlalchemy.func.test_f('a'))                                 
test_f(:test_f_1)                                                           

In [87]: print(sqlalchemy.func.test_f(a='a'))                               
test_f()                                                                    

我是不是漏掉了什么,还是说 func 根本不支持命名参数?

1 个回答

4

感谢Michael Bayer的建议,我找到了自己问题的解决办法:诀窍在于使用SQLAlchemy的编译器,以及一些正确的转义处理:

from psycopg2.extensions import adapt as sqlescape                          
import sqlalchemy                                                           
from sqlalchemy import select                                               
from sqlalchemy.ext.compiler import compiles                                
from sqlalchemy.sql.expression import ColumnClause                          


class MyFunc(ColumnClause):                                                 
    def __init__(self, *args, **kwargs):                                    
        self.kwargs = kwargs                                                
        super().__init__(*args)                                             


@compiles(MyFunc)                                                           
def compile_myfunc(element, compiler, **kw):                                
    s = ','.join("%s:=%s" % (k, sqlescape(v)) for k, v in element.kwargs.items())
    return "%s(%s)" % (element.name, s)                                     


def call(engine, func, **kwargs):                                           
    return engine.execute(select([MyFunc(func, **kwargs)]))                 


engine = sqlalchemy.create_engine('postgresql+psycopg2://lbolla@localhost/mytest')
print(call(engine, 'test_f', a='a').scalar())                               
print(call(engine, 'test_f', b='b').scalar())                               

撰写回答