在django raw sql中传递列表或元组作为参数



我有一个列表,想通过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")

我也试过通过传递元组,但没有用处。谁能帮我一下?

谢谢Vikram

至少在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'])

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

将列表转换为元组确实在Postgres中工作,尽管相同的代码在sqlite3与DatabaseError: near "?": syntax error下失败,因此似乎这是特定于后端。这行代码将变成:

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就不会报错)

这不是一个很好的解决方案,因为您必须确保您的"区域";为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])

我今天正好遇到了这个问题。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']。这样,您就允许数据库后端正确转义并可能对每个国家/地区代码进行编码。

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

最新更新