Как SqlAlchemy обрабатывает уникальные ограничения в определении таблицы - PullRequest
6 голосов
/ 20 апреля 2011

У меня есть таблица со следующим декларативным определением:

class Type(Base):
    __tablename__ = 'Type'
    id = Column(Integer, primary_key=True)
    name = Column(String, unique = True)
    def __init__(self, name):
        self.name = name

Столбец «имя» имеет уникальное ограничение, но я могу сделать

type1 = Type('name1')
session.add(type1)
type2 = Type(type1.name)
session.add(type2)

Итак, как видно, ограничение уникальности вообще не проверяется, поскольку я добавил в сеанс 2 объекты с одинаковыми именами.

Когда я делаю session.commit(), я получаю ошибку mysql, так как ограничение также находится в таблице mysql.

Возможно ли, что sqlalchemy заранее сообщит мне, что я не могу сделать это или идентифицирует его, и не вставляет 2 записи с одинаковым именем "name"? Если нет, следует ли мне хранить в памяти все существующие имена, чтобы я мог проверить, существуют ли они, прежде чем создавать объект?

Ответы [ 4 ]

9 голосов
/ 20 апреля 2011

SQLAlechemy не обрабатывает уникальность, потому что это невозможно сделать хорошим способом.Даже если вы отслеживаете созданные объекты и / или проверяете, существует ли объект с таким именем, существует условие гонки: любой другой процесс может вставить новый объект с только что проверенным именем.Единственное решение - заблокировать всю таблицу перед проверкой и снять блокировку после вставки (некоторые базы данных поддерживают такую ​​блокировку).

6 голосов
/ 25 апреля 2011

AFAIK, sqlalchemy не обрабатывает ограничения уникальности в поведении python.Эти объявления «unique = True» используются только для наложения ограничений таблицы уровня базы данных, и только тогда, когда вы создаете таблицу с помощью команды sqlalchemy, то есть

Type.__table__.create(engine)

или чего-то подобного.Если вы создадите модель SA для существующей таблицы, у которой фактически нет этого ограничения, это будет так, как если бы она не существовала.

В зависимости от вашего конкретного случая использования, вам, вероятно, придется использоватьшаблон как

try:
  existing = session.query(Type).filter_by(name='name1').one()
  # do something with existing
except:
  newobj = Type('name1')
  session.add(newobj)

или вариант, или вам просто нужно перехватить исключение mysql и восстановиться оттуда.

3 голосов
/ 08 августа 2012

Из документов

class MyClass(Base):
    __tablename__ = 'sometable'
    __table_args__ = (
            ForeignKeyConstraint(['id'], ['remote_table.id']),
            UniqueConstraint('foo'),
            {'autoload':True}
            )
1 голос
/ 13 февраля 2012

.one() выдает два вида исключений: sqlalchemy.orm.exc.NoResultFound и sqlalchemy.orm.exc.MultipleResultsFound

Вы должны создать этот объект, когда возникает первое исключение, если второе происходит, вы все равно облажались и не должны делатьхуже.

try:
  existing = session.query(Type).filter_by(name='name1').one()
# do something with existing
except NoResultFound:
  newobj = Type('name1')
  session.add(newobj)
...