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коли я спробую. Як я можу змусити це працювати?


2
Чи можете ви ініціалізувати двигун SA echo=Trueі побачити, що SQL виконується в поточний час? Те, що ви описуєте, повинно працювати і дати вам ідентифікатор, але може виникнути інша проблема, яка призводить до того, що f.id не буде None.
Павло Рєпін

Відповіді:


63

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

  1. Деталі вашого картографування
  2. Якщо в роботі використовуються якісь незвичайні примхи (наприклад, SQLite не генерує цілих значень для складеного первинного ключа)
  3. Що говорить випромінюваний SQL, коли ви вмикаєте ехо

1
Ви правильно, швидка перевірка оболонки показує, що вона заповнює поле первинного ключа значенням. Мені доведеться дослідити, чому це не працює на практиці.
Елофф

3
"ваш зразок коду повинен надавати значення" звучить так, як ви говорите: "ви повинні в першу чергу дати значення id", а не "той код, який ви мали працювати, як є". Мені стало зрозуміло, що ви маєте на увазі останнє, а не перше, лише після третього читання. Не могли б ви уточнити?
стохастичний

126

Я просто зіткнувся з тією ж проблемою, і після тестування я виявив, що НІКОЛІ з цих відповідей недостатньо.

В даний час, або як для 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 () НЕ працює як задокументована IMHO.
Марк

2
Оновлення, я повинен був використовувати sessionmaker(autoflush=True), що комбо w / refresh () надало мені ідентифікатор рядка #grrr
Марк

5
Якщо ви не розумієте різницю між flush()і commit(), ось гарне пояснення: stackoverflow.com/a/4202016/1252290
Епоха

2
@PlaidFan Замість flush()використання commit()та відразу після цього - оновіть його session.refresh(f), працює для мене, і я використовую версію SQLAlchemy0.6.7
Ricky Levi

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, оновити не потрібно. Після того, як ви вмикаєте, ви можете отримати доступ до поля ідентифікатора, sqlalchemy автоматично оновлює ідентифікатор, який автоматично генерується у бекенді

Я зіткнувся з цією проблемою і зрозумів точну причину після деякого розслідування, моя модель була створена з ідентифікатором як цілим полем, і в моїй формі ідентифікатор був представлений прихованим полем (оскільки я не хотів показувати ідентифікатор у своїй формі). Приховане поле за замовчуванням представлено у вигляді тексту. як тільки я змінив форму на integerfield з віджетом = hiddenInput ()) проблема була вирішена.


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

1

У мене колись виникли проблеми з призначенням 0id перед session.addметодом виклику . Ідентифікатор був правильно призначений базою даних, але правильний ідентифікатор не був вилучений із сеансу після session.flush().


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.