【问题标题】:Passing lists or tuples as arguments in django raw sql在 django raw sql 中传递列表或元组作为参数
【发布时间】:2011-10-17 05:26:08
【问题描述】:

我有一个列表,想通过 django raw sql 传递。

这是我的清单

region = ['US','CA','UK']

我在这里粘贴原始 sql 的一部分。

results = MMCode.objects.raw('select assigner, assignee from mm_code where date between %s and %s and country_code in %s',[fromdate,todate,region])

现在它给出了以下错误,当我在 django python shell 中执行它时

Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/usr/local/lib/python2.6/dist-packages/django/db/models/query.py", line 1412, in __iter__
query = iter(self.query)
File "/usr/local/lib/python2.6/dist-packages/django/db/models/sql/query.py", line 73, in __iter__
self._execute_query()
File "/usr/local/lib/python2.6/dist-packages/django/db/models/sql/query.py", line 87, in _execute_query
self.cursor.execute(self.sql, self.params)
File "/usr/local/lib/python2.6/dist-packages/django/db/backends/util.py", line 15, in execute
return self.cursor.execute(sql, params)
File "/usr/local/lib/python2.6/dist-packages/django/db/backends/mysql/base.py", line 86, in execute
return self.cursor.execute(query, args)
File "/usr/lib/pymodules/python2.6/MySQLdb/cursors.py", line 166, in execute
self.errorhandler(self, exc, value)
File "/usr/lib/pymodules/python2.6/MySQLdb/connections.py", line 35, in defaulterrorhandler
raise errorclass, errorvalue
DatabaseError: (1064, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ')' at line 1")

我也尝试过传递元组,但没有用。谁能帮帮我。

谢谢 维克拉姆

【问题讨论】:

  • 你能给我们你的应用试图执行的 sql 查询字符串吗?
  • 这是我的 sql 查询,它正在正确执行并且正在检索结果。 select assigner, assignee from mm_code where date between '2011-07-21' and '2011-07-31' and country_code in ('US','UK','CA')我尝试了很多组合但总是报错:-(
  • 您确定您的框架构建了相同的查询吗?
  • Yes.. 上面的查询在 Django Shell 中执行最新条件,但是当我给出 country_code 条件时,它给出错误。请帮帮我
  • 有人能回答这个问题吗

标签: sql django list tuples


【解决方案1】:

好吧,我不反对原始 sql,但您可以使用: MMCode.objects.filter(country_code__in=region, date__range=[fromdate,todate])

希望这会有所帮助。

【讨论】:

  • 我的查询非常大,我刚刚在这里粘贴了一个子查询。所以 ORM 在我的情况下没有用,而且我不擅长 ORM :-)
  • 由于您的原始 sql 在 django shell 中工作,它可能与您的列表有关。尝试调试,找到运行时形成的sql查询。
【解决方案2】:

这不是一个很好的解决方案,因为您必须确保您的“区域”值是正确的escaped for SQL。然而,这是我唯一可以使用 Sqlite 的东西:

sql = ('select assigner, assignee from mm_code '
    'where date between %%s and %%s and country_code in %s' % (tuple(region),))
results = MMCode.objects.raw(sql, [fromdate,todate])

【讨论】:

    【解决方案3】:

    将列表转换为元组在 Postgres 中确实有效,尽管相同的代码在带有 DatabaseError: near "?": syntax error 的 sqlite3 下失败,所以这似乎是特定于后端的。您的代码行将变为:

    results = MMCode.objects.raw('select assigner, assignee from mm_code where date between %s and %s and country_code in %s',[fromdate,todate,tuple(region)])

    我在一个干净的 Django 1.5.1 项目上测试了这个,在 bar/models.py 中有以下内容:

    from django.db import models
    
    class MMCode(models.Model):
        assigner = models.CharField(max_length=100)
        assignee = models.CharField(max_length=100)
        date = models.DateField()
        country_code = models.CharField(max_length=2)
    

    然后在shell:

    >>> from datetime import date
    >>> from bar.models import MMCode
    >>> 
    >>> regions = ['US', 'CA', 'UK']
    >>> fromdate = date.today()
    >>> todate = date.today()
    >>> 
    >>> results = MMCode.objects.raw('select id, assigner, assignee from bar_mmcode where date between %s and %s and country_code in %s',[fromdate,todate,tuple(regions)])
    >>> list(results)
    []
    

    (注意这里的查询行略有改动,使用Django创建的默认表名,并在输出中包含id列,这样ORM就不会报错)

    【讨论】:

      【解决方案4】:

      至少对于 PostgreSQL,列表/元组参数在 SQL 中被转换为数组,例如

      ARRAY['US', 'CA', 'UK']
      

      当它被插入到给定的查询中时,它会导致无效的 SQL -

      SELECT assigner, assignee FROM mm_code
      WHERE date BETWEEN '2014-02-01' AND '2014-02-05'
      AND country_code IN ARRAY['US', 'CA', 'UK']
      

      但是,SQL 中的 'in' 子句在逻辑上等价于 -

      SELECT assigner, assignee FROM mm_code
      WHERE date BETWEEN %s AND %s
      AND country_code = ANY(%s)
      

      ...当这个查询被参数填充时,生成的 SQL 是有效的并且可以工作 -

      SELECT assigner, assignee FROM mm_code
      WHERE date BETWEEN '2014-02-01' AND '2014-02-05'
      AND country_code = ANY(ARRAY['US', 'CA', 'UK'])
      

      我不确定这是否适用于其他数据库,以及这是否会改变查询的计划方式。

      【讨论】:

        【解决方案5】:

        我今天遇到了这个问题。 Django 变了(我们现在有了RawSQL() 和朋友们!),但通用的解决方案还是一样的。

        根据https://stackoverflow.com/a/283801/532513,一般的想法是在您的SQL 字符串中显式添加与region 数组中的元素相同数量的占位符。

        您的代码将如下所示:

        sql = 'select assigner, assignee from mm_code where date between %s and %s and country_code in ({0})'\
              .format(','.join([%s] * len(region)))
        results = MMCode.objects.raw(sql, [fromdate,todate] + region)
        

        然后,您的 sql 字符串将首先变为 ... between %s and %s and country_code in (%s, %s, %s) ...,而您的参数将有效地变为 [fromdate, todate, 'US', 'CA', 'UK']。这样,您可以让数据库后端正确转义并可能对每个国家/地区代码进行编码。

        【讨论】:

          猜你喜欢
          • 2014-11-02
          • 1970-01-01
          • 2022-01-02
          • 1970-01-01
          • 1970-01-01
          • 2014-12-17
          • 2011-06-18
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多