2015-11-17 31 views
2

我基於@ zzzeeek對this question的回答得到了我的代碼。 我擴展了一點,所以它考慮了Postgresql的NULL和ARRAY。如何在自定義編譯表達式中使用bindparam()?

class values(FromClause): 
    named_with_column = True 

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

    def _populate_column_collection(self): 
     # self._columns.update((col.name, col) for col in self._column_args) 
     for c in self._column_args: 
     c._make_proxy(self, c.name) 


@compiles(values) 
def compile_values(element, compiler, asfrom=False, **kw): 
    columns = element.columns 
    v = "VALUES %s" % ", ".join(
    "(%s)" % ", ".join(
     ((compiler.visit_array(elem)+'::'+str(column.type)) if isinstance(column.type, ARRAY) else 
     compiler.render_literal_value(elem, column.type)) 
     if elem is not None else compiler.render_literal_value(elem, NULLTYPE) 
     for elem, column in zip(tup, columns)) 
    for tup in element.list 
    ) 
    if asfrom: 
     if element.alias_name: 
      v = "(%s) AS %s (%s)" % (v, element.alias_name, (", ".join(c.name for c in element.columns))) 
     else: 
      v = "(%s)" % v 
    return v 

一切工作正常,直到它變成了我不能用「%」插入值 - 符號這個values子句 - 它們會內聯在聲明結果,這似乎會導致綁定問題

我如果不是render_literal_value()我們使用bindparam()我們可以避免這樣的錯誤。但@compiles下的所有內容都應該返回純文本,對嗎?我怎樣才能修改這個來獲取基於參數的查詢?

回答

1

我明白了。 bindparams的實際值保存在一個名爲SQLCompiler的對象中,該對象通常是特定於方言的。 Here(link to GitHub)就是bindparams在查詢編譯過程中獲取存儲在一個SQLCompiler實例

所以我的代碼片段的最終版本是這樣的:

class values(FromClause): 
    named_with_column = True 

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

    def _populate_column_collection(self): 
     # self._columns.update((col.name, col) for col in self._column_args) 
     for c in self._column_args: 
      c._make_proxy(self, c.name) 


@compiles(values) 
def compile_values(clause, compiler, asfrom=False, **kw): 
    def decide(value, column): 
     add_type_hint = False 
     if isinstance(value, array) and not value.clauses: # for empty array literals 
      add_type_hint = True 

     if isinstance(value, ClauseElement): 
      intermediate = compiler.process(value) 
      if add_type_hint: 
       intermediate += '::' + str(column.type) 
      return intermediate 

     elif value is None: 
      return compiler.render_literal_value(
       value, 
       NULLTYPE 
      ) + '::' + str(column.type) 
     else: 
      return compiler.process(
       bindparam(
        None, 
        value=compiler.render_literal_value(
         value, 
         column.type 
        ).strip("'") 
       ) 
      ) + '::' + str(column.type) 

    columns = clause.columns 
    v = "VALUES %s" % ", ".join(
     "(%s)" % ", ".join(
      decide(elem, column) 
      for elem, column in zip(tup, columns)) 
     for tup in clause.list 
    ) 
    if asfrom: 
     if clause.alias_name: 
      v = "(%s) AS %s (%s)" % (v, clause.alias_name, (", ".join(c.name for c in clause.columns))) 
     else: 
      v = "(%s)" % v 
    return v 
相關問題