sqlalchemy flush() и получить вставленный идентификатор?
Я хочу сделать что-то вроде этого:
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, когда я пытаюсь это сделать. Как я могу заставить это работать?
Дан
10 ответов
Ваш пример кода должен работать как есть. Sqlalchemy должен обеспечивать значение для f.id
, предполагая его автоматически генерирующийся столбец первичного ключа. атрибуты первичного ключа заполняются непосредственно в процессе flush(), поскольку они генерируются, и не требуется никакого вызова commit(). Таким образом, ответ здесь заключается в деталях вашего отображения, если есть какие-то странные особенности используемого бэкэнда (например, SQLite не генерирует целочисленные значения для составного первичного ключа) и / или что говорит испущенный SQL, когда вы включить эхо.
Я только что столкнулся с той же проблемой, и после тестирования я обнаружил, что ни одного из этих ответов не достаточно.
В настоящее время или в 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.
Вот как это сделать.
Спасибо всем. Я решил свою проблему, изменив сопоставление столбцов. Для меня, 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)
в порядке!
Последние пару часов/дней/независимо от того, я пытался заставить приведенные выше предложения работать. Изначально я написал все свои функции вставки так:
_add = User(id, user_name, email, ...)
Где все элементы в круглых скобках являются переменными для None, "user a", " [email protected] ",...
Это моя таблица пользователей:
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, autoincrement=True)
user_name = Column(String(50), unique=True, nullable=False)
email = Column(String(100), unique=True, nullable=False)
SQLAlchemy правильно обрабатывает запрос _add, так как вставляет запись с автоматически увеличивающимся идентификатором. Кроме того, как и должно быть, для столбца id не задано значение по умолчанию.
Я пробовал все вышеперечисленные варианты по-разному (с/без фиксации, с/без сброса, с/без обновления, один перед другим, тайм-ауты между операторами, вы называете это). Я даже несколько раз изменил все взаимодействие приложения/базы данных. Но во всех случаях «_add.id» либо возвращает 0, либо что-то вроде «Экземпляр» был удален, или его строка иначе отсутствует».
Только что я подумал: « Может быть, мне следует написать мой запрос _add немного по-другому, определив также имена столбцов для указанной таблицы », например:
_add = User(id=None, user_name=user_name, email=email, etc)
Чтобы подчеркнуть, обратите внимание: id= , user_name= , email= в запросе _add. Теперь, со следующими операторами в этом порядке, SQLAlchemy действительно возвращает вставленный идентификатор!
session.add(_add)
print(_add.id) <-- returns None
session.flush() <-- does **not** insert record into database, but does increment id,
waiting to be committed. Flush may be omitted, because
session.commit() unconditionally issues session.flush()*
print(_add.id) <-- returns incremented id
session.commit() <-- commit is needed to actually insert record into database
print(_add.id) <-- returns incremented id
Хотя ответ был предоставлен, мне не было ясно, что отсутствующие имена столбцов в запросе _add и, следовательно, моя лень были причиной моих проблем. Я надеюсь, что это может помочь кому-то избежать того же устранения неполадок...
Основное решение упоминалось в других гораздо более старых ответах, но в нем используется более новый асинхронный API.
с sqlalchemy==1.4 (стиль 2.0), похоже, работает следующее:
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.ext.asyncio import create_async_engine
engine = create_async_engine(
"postgresql+asyncpg://user:pass@localhost/db",
echo=False,
)
# expire_on_commit=False will prevent attributes from being expired
# after commit.
async_session = sessionmaker(
engine, expire_on_commit=False, class_=AsyncSession,
)
# default kwarg autoflush=True
async with async_session() as session:
async with session.begin():
f = Foo(bar='x')
session.add(f)
print(f.id)
# None
await session.flush()
print(f.id)
# not None
# commits transaction, closes session
В отличие от ответа, данного dpb, обновление не требуется. Как только вы очистите, вы можете получить доступ к полю id, sqlalchemy автоматически обновляет идентификатор, который автоматически генерируется на бэкэнде
Я столкнулся с этой проблемой и выяснил точную причину после некоторого исследования, моя модель была создана с идентификатором в качестве целочисленного поля, и в моей форме идентификатор был представлен скрытым полем (так как я не хотел показывать идентификатор в моей форме). Скрытое поле по умолчанию представлено в виде текста. как только я изменил форму на integerfield с widget=hiddenInput()), проблема была решена.
мой код работает так:
f = Foo(bar="blabla")
session.add(f)
session.flush()
session.refresh(f, attribute_names=[columns name that you want retrieve]
# so now you can access the id inserted, for example
return f.id # id inserted will be returned
Я столкнулся с той же проблемой отсутствия значений идентификатора после создания объектов при использовании .
К счастьюreturn_defaults
параметр вbulk_save_objects
добавляет все идентификаторы к объектам без необходимости вызоваflush
илиcommit
.
users: list[User] = [User(user_name=..., email=..., etc) for foo in bar]
session.bulk_save_objects(references, return_defaults=True)
assert users[0].id is not None # User.id is now populated.
У меня когда-то была проблема с назначением 0
идентифицировать перед звонком session.add
метод. Идентификатор был правильно назначен базой данных, но правильный идентификатор не был получен из сеанса после session.flush()
,
Вы должны попробовать использовать session.save_or_update(f)
вместо session.add(f)
,