sqlachemy:将bindparam与值一起使用

时间:2019-08-05 12:40:44

标签: sqlalchemy

SQLAlchemy核心。

我需要创建以下查询

    SELECT <expression>
    FROM (VALUES ('v1', 'v2')) AS v(x)
    INNER JOIN...

(我正在使用支持此标准SQL语法的PostgreSQL)

但是VALUES数据必须来自bindparam('values')。

我正在使用此答案中的values类 How to make use of bindparam() in a custom Compiled expression?


    from sqlalchemy.dialects.postgresql import ARRAY
    from sqlalchemy.ext.compiler import compiles
    from sqlalchemy.sql import true
    from sqlalchemy.sql.compiler import SQLCompiler
    from sqlalchemy.sql.elements import (
        BindParameter, literal, literal_column, quoted_name)
    from sqlalchemy.sql.functions import func
    from sqlalchemy.sql.expression import (
        and_, asc, bindparam, case, cast, or_, select)
    from sqlalchemy.sql.selectable import CTE, FromClause, Select
    from sqlalchemy.sql.sqltypes import (
        Boolean, Integer, Interval, Numeric, String, Text, Time)
    from sqlalchemy.schema import Column, Table


    class Values(FromClause):
        """Support FROM(VALUES ...)"""
        named_with_column = True

        def __init__(self, columns, *args, **kw):
            self._column_args = columns
            self.list = args
            self._hide_froms.append(self)
            self.alias_name = self.name = kw.pop('alias_name', None)

        def _populate_column_collection(self) -> None:
            for c in self._column_args:
                getattr(c, "_make_proxy")(self)


    @compiles(Values)
    def compile_values(element: Any,
                       compiler: SQLCompiler,
                       asfrom: bool =False,
                       **kw) -> str:
        """SQL Render instruction for Values,"""
        columns = element.columns
        if len(element.list) == 1 and isinstance(element.list[0],     BindParameter):
            v = "VALUES {}".format(
                compiler.process(element.list[0]))
        else:
            v = "VALUES {}".format(
                ", ".join(
                    "({})".format(
                        ", ".join(
                            compiler.render_literal_value(elem,     column.type)
                            for elem, column in zip(tup, columns)))
                    for tup in element.list))
        if asfrom:
            if element.alias_name:
                values = ", ".join(c.name for c in element.columns)
                v = f"({v}) AS {element.alias_name} ({values})"
            else:
                v = f"({v})"
        return v

这就是我的用法(逻辑比这更复杂,我只显示相关部分)。

    v = Values(
        [literal_column("cn", Text)], 
        bindparam("x"), 
        alias_name="v") 
    q = (
        select(["*"]).
        select_from(
            v.join(
                table, 
                onclause=v.c.cn == table.c.field))  
    engine.execute(q, x=("a", "b", "c"))

我得到的输出是

    SELECT *
    FROM (VALUES (('a'), ('b'), ('c'))) AS v (cn) 
    JOIN ...

代替

    SELECT *
    FROM (VALUES ('a'), ('b'), ('c')) AS v (cn) 
    JOIN ...

该如何解决?

0 个答案:

没有答案