Передача списков или кортежей в качестве аргументов в django raw sql - PullRequest
18 голосов
/ 01 августа 2011

У меня есть список, и я хочу передать через django raw sql.

Вот мой список

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

Я вставляю сюда часть raw 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])

Теперь он выдает следующую ошибку, когда я выполняю ее в оболочке Python django

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")

Я также попытался передать кортеж, но тамбесполезноМожет ли кто-нибудь помочь мне.

Спасибо Викрам

Ответы [ 5 ]

24 голосов
/ 25 февраля 2014

По крайней мере, для 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']

Тем не менее, предложение «in» в SQL логически эквивалентно -

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'])

Я не уверен, работает ли это в других базах данных, и влияет ли это на планирование запроса.

9 голосов
/ 20 сентября 2013

Преобразование списка в кортеж работает в 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)

затем в оболочке:

>>> 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 не жаловался)

3 голосов
/ 04 ноября 2011

Это не очень хорошее решение, потому что вы должны убедиться, что значения вашего «региона» правильно экранированы для 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])
1 голос
/ 28 сентября 2016

Я столкнулся именно с этой проблемой сегодня.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'].Таким образом, вы позволяете бэкэнду базы данных корректно экранировать и потенциально кодировать каждый из кодов стран.

0 голосов
/ 01 августа 2011

Ну, я не против raw sql, но вы можете использовать: MMCode.objects.filter(country_code__in=region, date__range=[fromdate,todate])

надеюсь, это поможет.

...