Я хочу получить объект из базы данных, если он уже существует (на основе предоставленных параметров), или создать его, если его нет.
Джанго get_or_create
(или источник ) делает это. Есть ли эквивалентный ярлык в SQLAlchemy?
В настоящее время я пишу это явно так:
def get_or_create_instrument(session, serial_number):
instrument = session.query(Instrument).filter_by(serial_number=serial_number).first()
if instrument:
return instrument
else:
instrument = Instrument(serial_number)
session.add(instrument)
return instrument
python
django
sqlalchemy
FogleBird
источник
источник
session.merge
: stackoverflow.com/questions/12297156/…Ответы:
Это в основном способ сделать это, нет быстрого доступа AFAIK.
Вы можете обобщить это конечно:
источник
try...except IntegrityError: instance = session.Query(...)
вокругsession.add
блока.Следуя решению @WoLpH, вот код, который работал для меня (простая версия):
Благодаря этому я могу получить любой объект моей модели.
Предположим, мой модельный объект:
Чтобы получить или создать мой объект, я пишу:
источник
commit
(или хотя бы использоватьflush
вместо него только a ). Это оставляет управление сессией вызывающей стороне этого метода и не рискует выпустить преждевременную фиксацию. Кроме того, использованиеone_or_none()
вместоfirst()
может быть немного безопаснее.Я играл с этой проблемой и в итоге получил довольно надежное решение:
Я только что написал довольно обширное сообщение в блоге обо всех деталях, но несколько довольно идей о том, почему я использовал это.
Распаковывается в кортеж, который сообщает вам, существовал объект или нет. Это часто может быть полезно в вашем рабочем процессе.
Функция дает возможность работать с
@classmethod
декорированными функциями создателя (и специфичными для них атрибутами).Решение защищает от условий гонки, когда к хранилищу данных подключено более одного процесса.
РЕДАКТИРОВАТЬ: я изменился
session.commit()
наsession.flush()
как объяснено в этом сообщении в блоге . Обратите внимание, что эти решения зависят от используемого хранилища данных (в данном случае Postgres).РЕДАКТИРОВАТЬ 2: Я обновил, используя {} в качестве значения по умолчанию в функции, так как это типичная ошибка Python. Спасибо за комментарий , Найджел! Если вам интересно узнать об этом, проверьте этот вопрос StackOverflow и этот пост в блоге .
источник
get_or_create
это не потокобезопасна. Это не атомно. Кроме того, Djangoget_or_create
возвращает флаг True, если экземпляр был создан, или флаг False в противном случае.get_or_create
это делает почти то же самое. Это решение также возвращаетTrue/False
флаг, чтобы сигнализировать, был ли объект создан или извлечен, и также не является атомарным. Однако безопасность потоков и атомарные обновления являются проблемой для базы данных, а не для Django, Flask или SQLAlchemy, и как в этом, так и в Django решениях решаются транзакциями в базе данных.IntegrityError
возвращаться,False
так как этот клиент не создал объект?Модифицированная версия отличного ответа Эрика
create_method
. Если созданный объект имеет отношения и ему назначаются члены через эти отношения, он автоматически добавляется в сеанс. Например, создайте abook
, который имеетuser_id
иuser
как соответствующие отношения, затем выполнениеbook.user=<user object>
внутриcreate_method
добавитbook
к сеансу. Это означает, чтоcreate_method
должно быть внутри,with
чтобы извлечь выгоду из возможного отката. Обратите внимание, чтоbegin_nested
автоматически запускается сброс.Обратите внимание, что при использовании MySQL уровень изоляции транзакции должен быть установлен,
READ COMMITTED
а неREPEATABLE READ
чтобы это работало. Get_or_create в Django (и здесь ) использует ту же стратегию, см. Также документацию по Django .источник
IntegrityError
повторный запрос все равно может завершиться неудачно сNoResultFound
уровнем изоляции MySQL по умолчанию,REPEATABLE READ
если сеанс ранее запрашивал модель в той же транзакции. Лучшее решение, которое я мог бы придумать, это позвонитьsession.commit()
перед этим запросом, что также не идеально, поскольку пользователь может этого не ожидать. Ссылочный ответ не имеет этой проблемы, так как session.rollback () имеет тот же эффект, что и запуск новой транзакции.commit
внутри этой функции, возможно, хуже, чем делатьrollback
, хотя для определенных случаев использования это может быть приемлемо.commit()
. Если я правильно понимаю код, это то, что делает Джанго., so it does not look like they try to handle this. Looking at the [source](https://github.com/django/django/blob/master/django/db/models/query.py#L491) confirms this. I'm not sure I understand your reply, you mean the user should put his/her query in a nested transaction? It's not clear to me how a
SAVEPOINT`, с которыми считывает влиянияREPEATABLE READ
. Если нет эффекта, то ситуация кажется неразрешимой, если эффект, то самый последний запрос может быть вложенным?READ COMMITED
, может быть, мне следует пересмотреть свое решение не трогать значения по умолчанию для базы данных. Я проверил, что восстановлениеSAVEPOINT
до того, как был сделан запрос, делает его так, как если бы этот запрос никогда не происходилREPEATABLE READ
. Поэтому я посчитал необходимым заключить запрос в предложении try во вложенную транзакцию, чтобы запрос в предложенииIntegrityError
кроме мог работать вообще.Это рецепт SQLALchemy делает работу красивой и элегантной.
Первое, что нужно сделать, это определить функцию, которой назначен Session для работы, и связать словарь с Session (), который отслеживает текущие уникальные ключи.
Пример использования этой функции был бы в mixin:
И, наконец, создание уникальной модели get_or_create:
Рецепт идет глубже в идею и предоставляет различные подходы, но я использовал этот с большим успехом.
источник
Наиболее близким семантически, вероятно, является:
не уверен, насколько кошерно полагаться на глобально определенный
Session
в sqlalchemy, но версия Django не требует подключения, так что ...Возвращенный кортеж содержит экземпляр и логическое значение, указывающее, был ли создан экземпляр (т. Е. Это False, если мы читаем экземпляр из БД).
Django
get_or_create
часто используется для обеспечения доступности глобальных данных, поэтому я делаю коммиты как можно раньше.источник
scoped_session
, что должно реализовывать поточно-ориентированное управление сессиями (существовало ли это в 2014 году?).Я немного упростил @Kevin. решение, чтобы избежать оборачивания всей функции в оператор
if
/else
. Таким образом, есть только одинreturn
, который я считаю чище:источник
В зависимости от принятого вами уровня изоляции, ни одно из приведенных выше решений не будет работать. Лучшее решение, которое я нашел, - это RAW SQL в следующей форме:
Это транзакционно безопасно независимо от уровня изоляции и степени параллелизма.
Осторожно: чтобы сделать его эффективным, было бы разумно иметь ИНДЕКС для уникального столбца.
источник