django + south + python: странное поведение при использовании текстовой строки, полученной в качестве параметра в функции - PullRequest
2 голосов
/ 14 апреля 2010

это мой первый вопрос.

Я пытаюсь выполнить SQL-запрос в django (южная миграция):

from django.db import connection
# ...
class Migration(SchemaMigration):
    # ...
    def transform_id_to_pk(self, table):
        try:
            db.delete_primary_key(table)
        except:
            pass
        finally:
            cursor = connection.cursor()

            # This does not work
            cursor.execute('SELECT MAX("id") FROM "%s"', [table])

            # I don't know if this works.
            try:
                minvalue = cursor.fetchone()[0]
            except:
                minvalue = 1
            seq_name = table + '_id_seq'

            db.execute('CREATE SEQUENCE "%s" START WITH %s OWNED BY "%s"."id"', [seq_name, minvalue, table])
            db.execute('ALTER TABLE "%s" ALTER COLUMN id SET DEFAULT nextval("%s")', [table, seq_name + '::regclass'])
            db.create_primary_key(table, ['id'])
    # ...

Я использую эту функцию следующим образом:

self.transform_id_to_pk('my_table_name')

Так и должно быть:

  1. Найти самый большой существующий идентификатор или 0 (он падает)
  2. Создать имя последовательности
  3. Создать последовательность
  4. Обновите поле идентификатора, чтобы использовать последовательность
  5. Обновить идентификатор как PK

Но происходит сбой и появляется сообщение об ошибке:

  File "../apps/accounting/migrations/0003_setup_tables.py", line 45, in forwards
    self.delegation_table_setup(orm)
  File "../apps/accounting/migrations/0003_setup_tables.py", line 478, in delegation_table_setup
    self.transform_id_to_pk('accounting_delegation')
  File "../apps/accounting/migrations/0003_setup_tables.py", line 20, in transform_id_to_pk
    cursor.execute(u'SELECT MAX("id") FROM "%s"', [table.encode('utf-8')])
  File "/Library/Python/2.6/site-packages/django/db/backends/util.py", line 19, in execute
    return self.cursor.execute(sql, params)
psycopg2.ProgrammingError: relation "E'accounting_delegation'" does not exist
LINE 1: SELECT MAX("id") FROM "E'accounting_delegation'"
                              ^

Для удобства я сократил пути к файлам.

Что означает "E'accounting_delegation"? Как я мог избавиться от этого?

Спасибо!

Карлос.

1 Ответ

4 голосов
/ 14 апреля 2010

Проблема в том, что вы используете параметризацию DB-API для вещей, которые не являются данными SQL. Когда вы делаете что-то вроде:

cursor.execute('INSERT INTO table_foo VALUES (%s, %s)', (col1, col2))

Модуль DB-API (интерфейс django для любой используемой вами базы данных, в данном случае) будет знать, что нужно экранировать содержимое 'col1' и 'col2' соответствующим образом и заменять% s на них. Обратите внимание, что вокруг% s нет кавычек. Но это работает только для SQL data , но не для SQL метаданных , таких как имена таблиц и имена последовательностей, потому что они должны быть заключены в кавычки по-разному (или не должны вообще). Когда вы это делаете

cursor.execute('INSERT INTO "%s" VALUES (%s, %s)', (tablename, col1, col2))

имя таблицы заключено в кавычки, как будто вы подразумеваете, что это строковые данные для вставки, и в итоге вы получите, например, «table_foo». Вам нужно разделить ваши метаданные SQL, которые являются частью запроса, и ваши данные SQL, которых нет, например:

sql = 'INSERT INTO TABLE "%s" VALUES (%%s, %%s)' % (tablename,)
cursor.execute(sql, (col1, col2))

Обратите внимание, что, поскольку параметром внешнего интерфейса DB-API django является 'pyformat' (он использует% s для заполнителей), вам необходимо избегать их при форматировании строки для создания SQL, который вы хотите выполнить. И обратите внимание, что это не защищает от атак SQL-инъекций, когда вы берете имя таблицы из небезопасного источника и не проверяете его.

...