Does this assert always pass or not? In other words, does SQLAlchemy save the order (in generating INSERT queries) when adding new objects to session?
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm.session import sessionmaker
from sqlalchemy.engine import create_engine
from sqlalchemy.types import Integer
from sqlalchemy.schema import Column
engine = create_engine('sqlite://')
Base = declarative_base(engine)
Session = sessionmaker(bind=engine)
session = Session()
class Entity(Base):
__tablename__ = 'entity'
id = Column(Integer(), primary_key=True)
Entity.__table__.create(checkfirst=True)
first = Entity()
session.add(first)
second = Entity()
session.add(second)
session.commit()
assert second.id > first.id
print(first.id, second.id)
None, in production I'm using postgresql, sqlite is for testing.
A session object is the handle to database. Session class is defined using sessionmaker() – a configurable session factory method which is bound to the engine object created earlier.
In the most general sense, the Session establishes all conversations with the database and represents a “holding zone” for all the objects which you've loaded or associated with it during its lifespan. It provides the interface where SELECT and other queries are made that will return and modify ORM-mapped objects.
SQLAlchemy supports the widest variety of database and architectural designs as is reasonably possible. Unit Of Work. The Unit Of Work system, a central part of SQLAlchemy's Object Relational Mapper (ORM), organizes pending insert/update/delete operations into queues and flushes them all in one batch.
Session. rollback() rolls back the current transaction.
After looking a bit at the SQLAlchemy source, it looks like add()
records when it was inserted: https://github.com/zzzeek/sqlalchemy/blob/master/lib/sqlalchemy/orm/session.py#L1719
The relevant snippet:
def _save_impl(self, state):
if state.key is not None:
raise sa_exc.InvalidRequestError(
"Object '%s' already has an identity - it can't be registered "
"as pending" % state_str(state))
self._before_attach(state)
if state not in self._new:
self._new[state] = state.obj()
state.insert_order = len(self._new) # THE INSERT ORDER IS SAVED!
self._attach(state)
And this is called from Session.add
=> self._save_or_update_state
=> self._save_or_update_impl
=> self._save_impl
.
It's then used in _sort_states
when saving: https://github.com/zzzeek/sqlalchemy/blob/master/lib/sqlalchemy/orm/persistence.py#L859
Unfortunately, this is only implementation-level proof. I couldn't find anything in the documentation that guarantees it...
Update: I've since looked a bit more into this, it turns out there's a concept called Unit of Work in SQLAlchemy that somewhat defines the order during a flush: http://www.aosabook.org/en/sqlalchemy.html (search for Unit of Work).
Within the same class, the order is indeed determined by the order that add
was called. However, you may see different orderings in the INSERTs between different classes. If you add object a
of type A
and later add object b
of type B
, but a
turns out to have a foreign key to b
, you'll see an INSERT for b
before the INSERT for a
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With