SQLAlchemy: успешная вставка, но затем возникает исключение

Я запускаю SQLAlchemy против FirebirdSQL, и когда я выполняю insert Команда в моем проекте, SQLAlchemy вызывает исключение при возврате из выполнения по соединению. Тем не менее, insert запрос строится и успешно выполняется. Запрос к базе данных показывает, что элементы фактически вставлены правильно.

Редактировать: я копаю в fbcore.py модуль, и проверка значения value а также vartype указывает на то, что проблема, вероятно, заключается в том, SEQUENCE Элемент, используемый для генерации идентификатора первичного ключа, возвращает его данные. vartype является SQL_LONG, но фактическое значение [<an integer>] где <an integer> значение, возвращаемое генератором последовательности, который я создал для автоматического увеличения первичного ключа (например, [14]). Это говорит мне о том, что проблема должна быть решена путем исправления этого, хотя я не уверен, как это сделать. Генератор работает правильно в самой базе данных, но вызывает проблемы при возврате в SQLAlchemy.

Смотрите ниже мою существующую реализацию и трассировку стека для деталей.

Мой код:

class Project:
    # (I've snipped project instantiation, where engine connection, table, etc. are configured)
    def save_project(self, id_=None, title=None, file_name=None, file_location=None):

        # Build the dictionary of values to store
        values = {}
        if title is not None:
            values['title'] = title

        if file_name is not None:
            values['file_name'] = file_name

        if file_location is not None:
            values['file_location'] = file_location

        # Simplification: I account for the case that there *is* data---skipping that here

        # Execute the correct kind of statement: insert or settings_update.
        if id_ is None:
            statement = self.table.insert()

        else:
            statement = self.table.update().where(self.table.c.id == id_)

        result = self.connection.execute(statement, values)

        # If we inserted a row, get the new primary key. Otherwise, return
        # the one specified by the user; it does not change on settings_update.
        project_id = result.inserted_primary_key if result.is_insert else id_

След:

  File "/Users/chris/development/quest/workspace/my_project/data/tables.py", line 350, in save_project
    result = self.connection.execute(statement, values)
  File "/Users/chris/.virtualenvs/my_project/lib/python3.3/site-packages/sqlalchemy/engine/base.py", line 720, in execute
    return meth(self, multiparams, params)
  File "/Users/chris/.virtualenvs/my_project/lib/python3.3/site-packages/sqlalchemy/sql/elements.py", line 317, in _execute_on_connection
    return connection._execute_clauseelement(self, multiparams, params)
  File "/Users/chris/.virtualenvs/my_project/lib/python3.3/site-packages/sqlalchemy/engine/base.py", line 817, in _execute_clauseelement
    compiled_sql, distilled_params
  File "/Users/chris/.virtualenvs/my_project/lib/python3.3/site-packages/sqlalchemy/engine/base.py", line 947, in _execute_context
    context)
  File "/Users/chris/.virtualenvs/my_project/lib/python3.3/site-packages/sqlalchemy/engine/base.py", line 1111, in _handle_dbapi_exception
    util.reraise(*exc_info)
  File "/Users/chris/.virtualenvs/my_project/lib/python3.3/site-packages/sqlalchemy/util/compat.py", line 168, in reraise
    raise value
  File "/Users/chris/.virtualenvs/my_project/lib/python3.3/site-packages/sqlalchemy/engine/base.py", line 940, in _execute_context
    context)
  File "/Users/chris/.virtualenvs/my_project/lib/python3.3/site-packages/sqlalchemy/dialects/firebird/kinterbasdb.py", line 106, in do_execute
    cursor.execute(statement, parameters or [])
  File "/Users/chris/.virtualenvs/my_project/lib/python3.3/site-packages/fdb/fbcore.py", line 3323, in execute
    self._ps._execute(parameters)
  File "/Users/chris/.virtualenvs/my_project/lib/python3.3/site-packages/fdb/fbcore.py", line 2991, in _execute
    self.__Tuple2XSQLDA(self._in_sqlda, parameters)
  File "/Users/chris/.virtualenvs/my_project/lib/python3.3/site-packages/fdb/fbcore.py", line 2782, in __Tuple2XSQLDA
    sqlvar.sqlscale)
  File "/Users/chris/.virtualenvs/my_project/lib/python3.3/site-packages/fdb/fbcore.py", line 2266, in _check_integer_range
    if (value < vmin) or (value > vmax):
TypeError: unorderable types: list() < int()

Я еще недостаточно знаком с SQLAlchemy, чтобы понять, почему это проблема; стиль моего высказывания в значительной степени идентичен стилю в уроке. Похоже, это проблема с передачей параметров - возможно, что-то об использовании dict, а не аргументов с ключевыми словами? Но в документах нет ничего о том, как обращаться с параметрами, которые указывают на то, что у меня здесь что-то не так - это кажется правильным из того, что я там вижу.

Я также попробовал это с self.table.insert().values(values) вместо того, чтобы передать values срок до execute метод, с теми же результатами (как я и ожидал).

Редактировать: я отмечаю от прочтения документации execute в fbcore.py что он вызывает ошибку TypeError, когда параметры, передаваемые методу, не передаются ни как список, ни как кортеж. Это изменение, которое еще не отражено в документации?

Редактировать 2: Как отмечается в комментарии, трассировка стека указывает, что он работает с драйвером kinterbasdb, хотя я явно настроил движок для работы с использованием fdb. Это также смущает меня.

2 ответа

Решение

Как и следовало ожидать, особенно когда я обнаружил, что проблема заключалась в том, что строка вставлялась как ожидалось, но затем вызывалась с UPDATE Вскоре после этого проблема была связана с кодом. Я возвращал результат как project_id (как вы можете видеть в приведенном выше коде), и по совершенно не связанной причине (связанной с сигналами Блинкера) метод снова вызывался с возвращенным значением project_id, который я установил так:

project_id = result.inserted_primary_key if result.is_insert else id_

Правильная версия этой строки немного отличается:

project_id = result.inserted_primary_key[0] if result.is_insert else id_

Из документации по SQLAlchemy (выделено мое):

Вернуть первичный ключ для только что вставленной строки.

Возвращаемое значение представляет собой список скалярных значений, соответствующих списку столбцов первичного ключа в целевой таблице.

Возвращаемое значение здесь должно быть списком, потому что первичные ключи могут быть комбинацией нескольких полей в базе данных. (Это должно было быть для меня очевидно; очевидно, что я не занимался серьезной работой с базами данных более года.) Поскольку первичный ключ в этом случае представляет собой одно значение, я просто выбрал это значение и возвратил его, и проблема заключается в том, что решена.

Конечно, теперь мне нужно разобраться с проблемой сигнала Блинкера - этот метод не должен вызываться дважды - но лучше всего...

Я изучал документацию по SQL Alchemy, и мне интересно, стоит ли вам это делать:

if id_ is None:
    statement = self.table.insert()

else:
    statement = self.table.update().where(self.table.c.id == id_)

statement = statement.values(title=title, file_name=file_name, file_location=file_location)

result = self.connection.execute(statement)

То есть: вместо передачи словаря в исполняемую часть сделайте его частью оператора (как показано выражениями вставки).

Другие вопросы по тегам