SQLAlchemy IntegrityError

У меня проблема с использованием SQLAlchemy с PySide (PyQt). Я пытаюсь открыть QtGui.QDialog, но когда я это делаю, SQLAlchemy выдает исключение:

Traceback (most recent call last):
  File "C:\Python27\lib\site-packages\preo\preodb\dbviewandmodel.py", line 32, in rowCount
    return len(self.rows())    
  File "C:\Python27\lib\site-packages\preo\preodb\dbviewandmodel.py", line 30, in rows
    return self.tableobj.query.all()
  File "C:\Python27\lib\site-packages\sqlalchemy-0.6.6-py2.7.egg\sqlalchemy\orm\query.py", line 1579, in all
return list(self)
  File "C:\Python27\lib\site-packages\sqlalchemy-0.6.6-py2.7.egg\sqlalchemy\orm\query.py", line 1688, in __iter__
    self.session._autoflush()
  File "C:\Python27\lib\site-packages\sqlalchemy-0.6.6-py2.7.egg\sqlalchemy\orm\session.py", line 862, in _autoflush
    self.flush()
  File "C:\Python27\lib\site-packages\sqlalchemy-0.6.6-py2.7.egg\sqlalchemy\orm\session.py", line 1388, in flush
    self._flush(objects)
  File "C:\Python27\lib\site-packages\sqlalchemy-0.6.6-py2.7.egg\sqlalchemy\orm\session.py", line 1469, in _flush
    flush_context.execute()
  File "C:\Python27\lib\site-packages\sqlalchemy-0.6.6-py2.7.egg\sqlalchemy\orm\unitofwork.py", line 302, in execute
    rec.execute(self)
  File "C:\Python27\lib\site-packages\sqlalchemy-0.6.6-py2.7.egg\sqlalchemy\orm\unitofwork.py", line 446, in execute
    uow
  File "C:\Python27\lib\site-packages\sqlalchemy-0.6.6-py2.7.egg\sqlalchemy\orm\mapper.py", line 1878, in _save_obj
    execute(statement, params)
  File "C:\Python27\lib\site-packages\sqlalchemy-0.6.6-py2.7.egg\sqlalchemy\engine\base.py", line 1191, in execute
    params)
  File "C:\Python27\lib\site-packages\sqlalchemy-0.6.6-py2.7.egg\sqlalchemy\engine\base.py", line 1271, in _execute_clauseelement
    return self.__execute_context(context)
  File "C:\Python27\lib\site-packages\sqlalchemy-0.6.6-py2.7.egg\sqlalchemy\engine\base.py", line 1302, in __execute_context
    context.parameters[0], context=context)
  File "C:\Python27\lib\site-packages\sqlalchemy-0.6.6-py2.7.egg\sqlalchemy\engine\base.py", line 1401, in _cursor_execute
    context)
  File "C:\Python27\lib\site-packages\sqlalchemy-0.6.6-py2.7.egg\sqlalchemy\engine\base.py", line 1394, in _cursor_execute
    context)
  File "C:\Python27\lib\site-packages\sqlalchemy-0.6.6-py2.7.egg\sqlalchemy\engine\default.py", line 299, in do_execute
    cursor.execute(statement, parameters)
sqlalchemy.exc.IntegrityError: (IntegrityError) ('23000', "[23000] [Microsoft][ODBC
SQL Server Driver][SQL Server]Violation of UNIQUE KEY
constraint 'UQ__users__F3DBC5720DAF0CB0'. Cannot insert duplicate key in
object 'dbo.users'. (2627) (SQLExecDirectW); [01000] [Microsoft][ODBC SQL Server
Driver][SQL Server]The statement has been terminated. (3621)") u'INSERT INTO users
(username, fullname, email, passwordmd5) OUTPUT inserted.id VALUES (?, ?, ?, ?)'
(None, None, None, None)

Это особенно беспокоит, потому что у меня нигде нет кода, который даже пытался бы вставлять записи в SQL; Я только когда-либо пытаюсь запросить данные из базы данных. Фактически, моя модель БД доступна только для чтения в отношении того, что делают PySide/PyQt (т. е. я использую модель/представление QtGui.QTableView, и в этой модели нет функции insertRows).

Я понятия не имею, что происходит и как это решить - опять же, у меня вообще нет кода для изменения записей SQL, но все же SQLAlchemy пытается вставить пустые записи в одну из моих таблиц SQL. Все, что я вижу на заднем плане, это то, что модель данных QTableView МНОГО запрашивает базу данных. Просто кажется, что когда я открываю этот QDialog (в котором есть некоторый код для запроса некоторого столбца таблицы), возникает эта ошибка. Как ни странно, это непоследовательно, иногда всплывающее окно появляется перед исключением, иногда всплывающее окно появляется после исключения. При нормальных обстоятельствах модель данных QTableView прекрасно работает, но не тогда, когда я открываю это диалоговое окно (и, по иронии судьбы, всплывающее окно вообще не использует никаких QTableView, только стандартные виджеты, такие как QLineEdit, QTextEdit и т. д.)

Если это поможет, я использую Python 2.7 с SQLAlchemy 0.6.6 (также с Elixir 0.7.1) и PySide 1.0.0 (и PyQt4 4.8.3). У меня Windows 7 с SQL 2008 R2 (Express). И да, я пытался перезагрузить компьютер, но проблема все еще возникает после перезагрузки. Я не хочу публиковать больше кода, потому что у меня его много в этом конкретном проекте, и я не могу определить проблему как-то конкретно.

Я надеюсь, что кто-то может знать о странностях в SQLAlchemy и/или PyQt, которые могут быть связаны с этим. Я также надеюсь, что смогу продолжать использовать SQLAlchemy, поскольку у меня построена большая модель данных; На данный момент я не хочу отказываться от этого и использовать функции PyQt SQL.


person Raceyman    schedule 16.03.2011    source источник
comment
Оператор select может ничего не делать, но, вероятно, что-то происходит с сеансом перед запросом. Когда запрос выполняется, он сбрасывает все изменения, которые еще предстоит сделать (например, вставка пользователя). Можем ли мы увидеть остальную часть файла dbviewandmodel.py?   -  person Mark Hildreth    schedule 17.03.2011
comment
Я все еще не делал никаких модификаций базы данных, когда это происходило (никаких вставок, никаких правок каких-либо данных в этой базе данных), просто запрашивал данные. Я думаю, что нашел способ НЕ вызвать этот сбой (на самом деле это не исправление, скорее, я могу сделать так, чтобы этого не происходило, не делая что-то конкретное). Я добавлю ответ, так как он немного затянут.   -  person Raceyman    schedule 17.03.2011


Ответы (1)


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

Во всяком случае, то, что, как я думаю, происходило, было связано с моей моделью данных SQLAlchemy и тем, как я к ней обращался, вот фрагмент этой модели:

from elixir import *

metadata.bind = 'mssql+pyodbc://username:password/dbname'
metadata.bind.echo = False

class Users(Entity):
    using_options(tablename = 'users')
    username = Field(String(50), unique=True)
    fullname = Field(String(255))
    email = Field(String(255))
    passwordmd5 = Field(String(32))
    def __repr__(self):
        return "<Users ({})({})({})>".format(self.username, self.fullname, self.email)
    def prettyname(self):
        return {'username':'User Name', 'fullname':'Full Name', 'email':'Email Address', 'passwordmd5':'$hidden$'}

В моем коде мне нужен был способ получить «красивые» имена меток для графического интерфейса без необходимости жестко кодировать это в графическом интерфейсе (я пытался создать динамический способ создания форм графического интерфейса). Итак, я добавил метод «красивое имя» в свою модель данных, чтобы дать мне некоторые метаданные для конкретного приложения в этой модели данных. Все, что я делаю, это возвращаю словарь элементов.

У меня была вторичная проблема в том, что иногда мне нужно было получить эти данные из экземпляра класса для пользователей, а иногда и для результата запроса для пользователей (например, Users.get_by(id=1)). Как оказалось, получить эти данные нужно было двумя способами. В экземплярах класса мне нужно было получить значение таким образом:

prettyname = Users().prettyname()['username']

Но когда я использовал результаты запроса, это было:

prettyname = queryresult.prettyname()['username']

У SQLAlchemy, похоже, возникла реальная проблема, когда я использовал прежний метод (метод экземпляра класса), поскольку он использовался каждый раз, когда я видел сбой. Когда я использовал последний экземпляр, я никогда не видел сбоев. Тем не менее, мне нужен был доступ к этим метаданным в экземпляре класса.

Исправление, или я должен сказать, что получилось исправить это, пришло из другой статьи Stackoverflow (спасибо всем в Stackoverflow - без вас я был бы ничем). Я изменил структуру dbmodel:

class Users(Entity):
    using_options(tablename = 'users')
    username = Field(String(50), unique=True, info={'prettyname':'User Name'})
    fullname = Field(String(255), info={'prettyname':'Full Name'})
    email = Field(String(255), info={'prettyname':'Email Address'})
    passwordmd5 = Field(String(32), info={'hidden':True})
    def __repr__(self):
        return "<Users ({})({})({})>".format(self.username, self.fullname, self.email)

Это позволяет мне использовать обычный метод самоанализа для получения данных словаря в информационном аргументе, независимо от того, смотрю ли я на экземпляр класса или на результат запроса. В этом случае я использую метод «.table» либо для класса, либо для результата запроса, затем получаю нужный мне столбец (.c), затем использую метод .info этого столбца для возврата словаря.

В любом случае, теперь SQLAlchemy больше не пытается произвольно вставлять строки в базу данных.

person Raceyman    schedule 17.03.2011
comment
Поскольку вы никогда не публиковали точный код, вызывающий проблему, я не могу быть в этом уверен. Однако у вас есть фрагмент выше, который читает prettyname = Users().prettyname()['username']. Поскольку вы используете Elixer, эта строка может привести к записи в таблицу пользователей. Users() создаст новый экземпляр Users. Elixer добавит это к сеансу. Запрос, выполненный после этого вызова, попытается зафиксировать новый экземпляр Users. Поэтому, если бы этот фрагмент был в вашем коде, вставка в таблицу пользователей была бы правильным поведением. - person Mark Hildreth; 17.03.2011
comment
Это имеет смысл, особенно если учесть, что я не мог точно сказать, в каком месте кода возникает проблема. Эта строка кода была настолько близкой, насколько я мог понять, где могла быть проблема. Я постараюсь избегать использования конструкции Users() в будущем. Спасибо! - person Raceyman; 17.03.2011