#sqlalchemy #unique
#sqlalchemy #уникальный
Вопрос:
У меня есть таблица со следующим декларативным определением:
class Type(Base):
__tablename__ = 'Type'
id = Column(Integer, primary_key=True)
name = Column(String, unique = True)
def __init__(self, name):
self.name = name
Столбец «name» имеет уникальное ограничение, но я могу сделать
type1 = Type('name1')
session.add(type1)
type2 = Type(type1.name)
session.add(type2)
Итак, как можно видеть, ограничение уникальности вообще не проверяется, поскольку я добавил в сеанс 2 объекта с одинаковым именем.
Когда я это делаю session.commit()
, я получаю ошибку mysql, поскольку ограничение также находится в таблице mysql.
Возможно ли, что sqlalchemy заранее сообщает мне, что я не могу его создать, или идентифицирует его и не вставляет 2 записи с одинаковым столбцом «name»? Если нет, должен ли я сохранить в памяти все существующие имена, чтобы я мог проверить, существуют они или нет, перед созданием объекта?
Ответ №1:
SQLAlechemy не обрабатывает уникальность, потому что это невозможно сделать хорошим способом. Даже если вы отслеживаете созданные объекты и / или проверяете, существует ли объект с таким именем, существует условие гонки: любой другой процесс может вставить новый объект с именем, которое вы только что проверили. Единственное решение — заблокировать всю таблицу перед проверкой и снять блокировку после вставки (некоторые базы данных поддерживают такую блокировку).
Комментарии:
1. Я буду единственным, кто вставляет туда данные, поэтому я позабочусь о том, чтобы этого не произошло. Мой вопрос конкретно о том, как sqlalchemy обрабатывает уникальность поля
2. @duduklein Он не обрабатывает уникальность. И мой ответ описывает, почему.
Ответ №2:
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:
class MyClass(Base):
__tablename__ = 'sometable'
__table_args__ = (
ForeignKeyConstraint(['id'], ['remote_table.id']),
UniqueConstraint('foo'),
{'autoload':True}
)
Комментарии:
1. В чем разница между этим и использованием
unique=True
, представленным в исходном вопросе?2.
unique=True
не предоставляет возможности указать уникальное имя для индекса, что вызовет проблемы с alembic. Смотрите: alembic.sqlalchemy.org/en/latest/naming.html3. Хорошо, но отвечает ли это на первоначальный вопрос? Я думаю, что могло бы быть полезно еще несколько объяснений вашего отношения к вопросу OP.
4. Не уверен, но этому вопросу уже почти десять лет, и это не принятый ответ.
5. это не значит, что его все еще не часто обнаруживают. Я просто пытаюсь понять ваш ответ и объяснить мой -1.
Ответ №4:
.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)