sqlalchemy flush () и вставить идентификатор?

114

Я хочу сделать что-то вроде этого:

f = Foo(bar='x')
session.add(f)
session.flush()

# do additional queries using f.id before commit()
print f.id # should be not None

session.commit()

Но f.idэто Noneкогда я попробую. Как я могу заставить это работать?

Eloff
источник
2
Можете ли вы инициализировать механизм SA echo=Trueи посмотреть, какой SQL выполняется во время сброса? То, что вы описываете, должно работать и давать вам идентификатор, но может быть другая проблема, из-за которой f.id имеет значение None.
Павел Репин

Ответы:

63

Ваш пример кода должен был работать как есть. SQLAlchemy должен предоставлять значение f.id, предполагая, что это автоматически генерируемый столбец первичного ключа. Атрибуты первичного ключа заполняются непосредственно в flush()процессе по мере их создания, и при этом не commit()требуется вызова . Итак, ответ здесь заключается в одном или нескольких из следующих пунктов:

  1. Детали вашего отображения
  2. Если есть какие-либо странные особенности используемого бэкэнда (например, SQLite не генерирует целочисленные значения для составного первичного ключа)
  3. Что говорит сгенерированный SQL, когда вы включаете эхо
Zzzeek
источник
1
Вы правы, быстрая проверка в оболочке показывает, что он заполняет поле первичного ключа значением. Придется выяснить, почему это не работает на практике.
Eloff
3
«ваш примерный код должен предоставлять значение» звучит так, как будто вы говорите «вы должны в первую очередь указать значение для id», а не «этот код, который у вас должен был работать, как есть». Только после третьего чтения мне стало ясно, что вы имели в виду второе, а не первое. Не могли бы вы уточнить?
стохастик
127

Я только что столкнулся с той же проблемой и после тестирования обнаружил, что НИКАКОГО из этих ответов недостаточно.

В настоящее время или в sqlalchemy .6+ существует очень простое решение (я не знаю, существует ли оно в предыдущей версии, хотя я думаю, что это так):

session.refresh ()

Итак, ваш код будет выглядеть примерно так:

f = Foo(bar=x)
session.add(f)
session.flush()
# At this point, the object f has been pushed to the DB, 
# and has been automatically assigned a unique primary key id

f.id
# is None

session.refresh(f)
# refresh updates given object in the session with its state in the DB
# (and can also only refresh certain attributes - search for documentation)

f.id
# is the automatically assigned primary key ID given in the database.

Вот как это сделать.

РОП
источник
8
Это приближается к ответу, который может сработать для меня, но я получаю следующую ошибку: InvalidRequestError: не удалось обновить экземпляр «<....>». После сброса выясняется, что экземпляр просто больше не существует. Цените любое понимание.
PlaidFan
1
Ты только что спас мою задницу. Я не думаю, что когда-нибудь снова буду использовать ORM от Django. Команда flush () НЕ работает, как задокументировано ИМХО.
Марк
2
Обновление, которое мне пришлось использовать sessionmaker(autoflush=True), эта комбинация с обновлением () предоставила мне идентификатор строки. #grrr
Марк
5
Если вы не понимаете разницу между flush()и commit(), вот хорошее объяснение: stackoverflow.com/a/4202016/1252290
Epoc
2
@PlaidFan Вместо flush()использования commit()и сразу после этого - обновите его session.refresh(f), у меня работает, и я использую версию SQLAlchemy0.6.7
Рики Леви
20

Спасибо всем. Я решил свою проблему, изменив отображение столбцов. Для меня autoincrement=Trueэто обязательно.

происхождение:

id = Column('ID', Integer, primary_key=True, nullable=False)

после изменения:

id = Column('ID', Integer, primary_key=True, autoincrement=True, nullable=True)

затем

session.flush()  
print(f.id)

в порядке!

полокс
источник
6

в отличие от ответа dpb, обновление не требуется. после промывки вы можете получить доступ к полю id, sqlalchemy автоматически обновит идентификатор, который автоматически генерируется на бэкэнде

Я столкнулся с этой проблемой и выяснил точную причину после некоторого расследования, моя модель была создана с идентификатором как целочисленное поле, а в моей форме идентификатор был представлен с помощью скрытого поля (поскольку я не хотел показывать идентификатор в своей форме). Скрытое поле по умолчанию представлено в виде текста. как только я изменил форму на целочисленное поле с помощью widget = hiddenInput ()), проблема была решена.

Райан
источник
1
Как указано, у меня работал только refresh (). При выполнении миграции данных мне понадобился идентификатор строки в цикле для заполнения FK. Я пробовал каждую комбинацию фиксации, сброса, взлома сеанса и обновления () - единственное, что сработало. Мои данные очень чистые, и я просто обнаружил, что SQLA на самом деле не так хорош (имея значительный опыт как минимум в 5 основных ORMS). Просто завершите более 5 часов, пытаясь получить идентификатор строки для add () -> commit () / flush ().
Марк
1

Однажды у меня была проблема с назначением 0id перед вызовом session.addметода. Идентификатор был правильно назначен базой данных, но после этого правильный идентификатор не был получен из сеанса session.flush().

бабки
источник
-7

Вам следует попробовать использовать session.save_or_update(f)вместо session.add(f).

Мохит Ранка
источник
1
save_or_updateустарел с 0.5 или около того. session.add()должен это сделать.
Павел Репин