Python Django从给定列表构建原始SQL查询

时间:2017-11-02 14:00:08

标签: python sql django postgresql

我尝试通过执行原始SQL查询从我的数据库后端获取一些信息。我需要进行计算(将一个字段乘以一个因子,截断它)并将其以高效的方式转换为JSON。这就是为什么我选择不在这里使用(Django)ORM的原因。

with connection.cursor() as cursor:
    cursor.execute(
      '''
      SELECT json_build_object(
        'data', array_agg(r)
      )
      FROM (
        SELECT
          time,
          trunc(column1 * 100, 2) as COL1,
          trunc(column2 * 100, 2) as COL2,
          [...]
        FROM measurements
          WHERE device_id=%s
          ORDER BY time DESC
          LIMIT 5
      ) r
      ''',
      [device_id]
  )
  result = cursor.fetchall()

我需要从以下列表中调整上述声明:

[
  {'column': 'column1', 'factor': 100, 'description': 'COL1', 'trunc': 2},
  {'column': 'column2', 'factor': 100, 'description': 'COL2', 'trunc': 2},
  [..]
]

由于我不熟悉python语法,但我想知道是否有一个优雅的解决方案来创建这样的语句。我知道我可以循环遍历dicts列表并附加查询,但这并不适合我。任何建议将不胜感激。

我正在考虑这样的事情:

['trunc({} * {}, {}) as {}'.format(
  d['column'], d['factor'], d['trunc'], d['description']) for d in l
]

1 个答案:

答案 0 :(得分:1)

如果您使用的是psycopg 2.7,则可以使用sql模块,这将使一切安全

from psycopg2 import sql

query = sql.SQL('''
  SELECT json_build_object(
    'data', array_agg(r)
  )
  FROM (
    SELECT time, {}
    FROM measurements
      WHERE device_id=%s
      ORDER BY time DESC
      LIMIT 5
  ) r
''')

truncated_rows = sql.SQL(', ').join(
    sql.SQL('trunc({} * {}, {}) as {}').format(
        sql.Identifier(d['column']),
        sql.Literal(d['factor']),
        sql.Literal(d['trunc']),
        sql.Identifier(d['description'])
    )
    for d in l
)

with connection.cursor() as cursor:
    cursor.execute(query.format(truncated_rows), [device_id])

使用2.6及更早版本,您将不得不回退到python字符串格式

','.join(
    'trunc({column} * {factor}, {trunc}) as {description}'.format(**data)
    for data in
    [
        {'column': 'column1', 'factor': 100, 'description': 'COL1', 'trunc': 2},
        {'column': 'column2', 'factor': 100, 'description': 'COL2', 'trunc': 2},
    ]
)