How can I, using sqlalchemy, do something like this?
user = session.query("select * from user")
if user.state == "active"
session.query("update user set state = 'inactive' where id = %d" % user.id)
I need the select and update to be one atomic operation. Another program should not be able to select/update the user while the operation is going.
How can I do it concurrently?
Note: I need to know if we succeeded in change the state or not.
How can I achieve that?
Am I doing it wrong?
Should it be a stored procedure?
Should I use database "lock"?
You can do this by setting an isolation level of Serializable, which can be done on a specific session with SQLAlchemy using session.connection(execution_options={'isolation_level': 'SERIALIZABLE'}). If two connections conflict (they both read before the other wrote), committing the transaction will fail, and you can just loop until it goes through.
from sqlalchemy import Column, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy.types import Boolean, Integer, String
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
active = Column(Boolean, nullable=False)
url = 'postgresql://postgres#localhost/test'
engine = create_engine(url, echo=True)
if not engine.dialect.has_table(engine.connect(), 'users'):
Base.metadata.create_all(bind=engine)
Session = sessionmaker(bind=engine)
session = Session()
session.add(User(name="remram", active=True))
session.commit()
Session = sessionmaker(bind=engine)
session = Session()
session.connection(execution_options={'isolation_level': 'SERIALIZABLE'}) # Commenting this line will make this unsafe in a concurrent environment
user = session.query(User).filter(User.name == "remram").one()
if user.active:
user.active = False
raw_input() # So you can run this twice
session.commit()
If your database supports SELECT FOR UPDATE
(ie: PostgreSQL) you could use:
user = session.query("select * from user where id = %d for update" % theId)
if user.state == "active"
session.query("update user set state = 'inactive' where id = %d" % user.id)
Related
I'm using SQLAlchemy to connect to an in-memory SQLite database.
In my main.py file I have
engine = create_engine('sqlite:///:memory:', echo = True)
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
sess = Session()
After this I create a new thread, where I pass the session to
thread = MyThread(sess)
thread.start()
I have created my tables in tables.py like this
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Float
Base = declarative_base()
class My_Entry(Base):
__tablename__ = 'my_entry'
id = Column(Integer, primary_key=True)
name = Column(String)
url = Column(String)
My_Thread looks like this
class MyThread(Thread):
def __init__(sess):
Thread.__init__(self)
self.sess = sess
def run(self):
last_entry_url = self.sess.query(My_Entry).filter_by(name=key).all()[-1]
I try to query the database in the run function of my thread
last_entry_url = self.sess.query(My_Entry).filter_by(name=key).all()[-1]
but this throws an error
(sqlite3.OperationalError) no such table: my_entry
But creating the tables is one of the first things I do?
Indeed, using the echo=True gives me
CREATE TABLE my_entry (
id INTEGER NOT NULL,
name VARCHAR,
url VARCHAR,
PRIMARY KEY (id)
)
I have a sleep statement before running MyThread. My other tables work just fine, it's only this one table that doesn't.
bonus question: is there another way to get the last entry instead of all()[-1]? I assume there is but I didn't find anything
I don't understand why I have this error. Please explain the error.
I used official documentation.
I run Pipenv virtual env:
python 3.8.2
sqlalchemy 1.3.16
You can try run this code too.
import enum
from sqlalchemy import create_engine, Column, Integer, String, Enum
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
engine = create_engine('sqlite:///:memory:', echo=True)
Base = declarative_base()
Session = sessionmaker(bind=engine)
session = Session()
class Type(str, enum.Enum):
ONE = "one"
TWO = "two"
class Item(Base):
__tablename__ = 'items'
id = Column(Integer, primary_key=True, index=True)
name = Column(String, unique=True, index=True)
type = Column(Enum(Type), default=Type.ONE, nullable=False)
item = Item(name="item_name", type="one")
session.add(item)
print(Item.__table__)
session.commit()
for name in session.query(Item.name):
print(name)
I added:
engine = create_engine('sqlite:///:memory:', echo=True)
Base = declarative_base()
Base.metadata.create_all(bind=engine)
Session = sessionmaker(bind=engine)
session = Session()
It creates the tables in the database (there are ways of using SQLAlchemy with preexisting tables, so an explicit instruction isd necessary).
In case it ends up helping someone, my issue was due to using an in memory sqlite db to unittest an API. Pytest would set up the database and tables with one connection while the API would create its own separate connection when the test were hitting endpoints. This ultimately solved it for me.
engine = create_engine("sqlite:///:memory:", poolclass=StaticPool, connect_args={'check_same_thread': False})
Ref: https://docs.sqlalchemy.org/en/14/dialects/sqlite.html#using-a-memory-database-in-multiple-threads
I'm newer in SQLAlchemy I use some examples to create table and insert information to it and it's working 100% .
But what I didn't find is some example for how can I update & delete some information from the database.
What I'm doing is :
from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
Base = declarative_base()
## create
class Person(Base):
__tablename__ = 'person'
id = Column(Integer, primary_key=True)
name = Column(String(250), nullable=False)
engine = create_engine('sqlite:///database.db')
Base.metadata.create_all(engine)
## insert
Base.metadata.bind = engine
DBSession = sessionmaker(bind=engine)
session = DBSession()
new_person = Person(name='new person')
session.add(new_person)
session.commit()
## fetch
getperson = session.query(Person).first()
print getperson.name
# this will print : new person
# I need some example to how can I update and delete this : new person
So in this code it'll print "new person" my question is how can I update or delete it ?
Here's some example on each CRUD operation in sqlalchemy (ommiting Create, Read as you already know how to perform those):
First, necessary imports and configs for any operation:
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
# Category, Item, User are my tables
from database_setup import Base, Category, Item, User
# Generating session to connect to the db's ORM
engine = create_engine('sqlite:///catalogwithusers.db') # my db
Base.metadata.bind = engine
DBSession = sessionmaker(bind = engine)
session = DBSession()
Then peforming an update:
# Get the item filtering by it's id using a one() query on Item table
# If query is not empty, update the attributes, add query to session and commit
q = session.query(Item).filter_by(id=item_id).one()
if q != []:
q.name = edited_name
q.description = edited_description
session.add(q)
session.commit()
Finally, performing a deletion:
# Again get the item similarly to the example above
# Then if query returned results, use the delete method and commit
q = session.query(Item).filter_by(id=item_id).one()
if q != []:
session.delete(q)
session.commit()
These examples are taken from here. I suggest you have a look. ORM Creation is inside database_setup.py and CRUD ops are performed inside project.py and populatecatalog.py.
As the title says.
Here are the codes.
from sqlalchemy import Column, ForeignKey, Integer, String, DateTime, func, Boolean
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import scoped_session
from sqlalchemy import create_engine
import sqlalchemy.exc
from sqlalchemy import event
from settings import DB_HOST
def return_a_scoped_session():
engine = create_engine(DB_HOST)
session_factory = sessionmaker(bind=engine)
db_session = scoped_session(session_factory)
return db_session()
Base = declarative_base()
class MyClass(Base):
"""Doc string for MyClass"""
__tablename__ = 'my_table'
file_name = Column(String(512), nullable=True)
class Aria2Jobs(Base):
__tablename__ = 'nh_downloading_jobs'
id = Column(Integer, primary_key = True)
file_name = Column(String(512), nullable=True)
is_verified = Column(Boolean, default=False, nullable=True)
def check_if_verified(self):
if self.is_verified:
# create an instance
a_job= MyClass(file_name=self.file_name)
_session = return_a_scoped_session()
_session.add(a_job)
_session.commit()
_session.close()
# event
#event.listens_for(Aria2Jobs.is_verified, 'set')
def send_to_jsonpyes_jobs(target, value, oldvalue, initiator):
target.check_if_verified()
# error is when I set a property of an object (this property will trigger an event 'set' and the event will try to commit a session.
session = return_a_scoped_session()
row = session.query(Aria2Jobs).first()
row.is_verified = True
session.add(row)
# the error came out
#
# sqlalchemy.exc.invalidrequesterror object is already attached to session
session.commit()
# How to commit another object in a property of an object?
How to commit another object in a property of an object in sqlalchemy?
As you can see, when I tried session.add(row),
error:
sqlalchemy.exc.invalidrequesterror object is already attached to session
I don't know which session the row is attached to.
I want to run the function check_if_verified
-- latest error --
sqlalchemy.exc.InvalidRequestError: Object '<Aria2Jobs at 0x7fad3a635050>' is already attached to session '1' (this is
Just remove session.add(row). Becouse you used session.query the row object is already in your session.
To save the data is enough to run session.commit function
You're using scoped_session incorrectly. What's happening here is each time you call return_a_scoped_session() it's returning a new session, with a completely new engine. row is being added to a different session somewhere else. (In fact, the code you posted doesn't even show the other places it's being added; I can't reproduce your error with the code you posted.) The solution is to fix your scoped_session:
engine = create_engine(DB_HOST)
Session = scoped_session(sessionmaker(bind=engine))
def return_a_scoped_session():
return Session()
I have a table and populating it with object list then I need to use their IDs, but I am getting an
Instance <location at 0x457f3b0> is not bound to a Session; attribute refresh operation cannot proceed
error.
I am populating a list with objects and send it to a function to insert all at once. Then I try to use IDs.
Here is my insert all function:
def insertlocations(locationlist):
session.add_all(locationlist)
session.commit()
session.close()
then I try to get IDs:
insertlocations(neighbourhoodlist)
session.flush(neighbourhoodlist)
for neighbourhood in neighbourhoodlist:
print neighbourhood.locationid
Session is global by the way. Any further info needed?
The data are inserted, as I look in the MySQL table.
Most likely your problem is that you already close() the session in your insertlocations() function.
When you then access neighbourhood.locationid, the session is closed and thatneighbourhood object isn't bound to a session any more.
For example, this should work:
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import sessionmaker
engine = create_engine('sqlite:///example.db')
engine.echo = True
Base = declarative_base()
class Location(Base):
__tablename__ = 'locations'
locationid = Column(Integer, primary_key=True)
name = Column(String)
address = Column(String)
def __init__(self, name, address):
self.name = name
self.address = address
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
def insertlocations(locationlist):
session.add_all(locationlist)
session.commit()
loc1 = Location('loc1', 'Foostreet 42')
loc2 = Location('loc2', 'Barstreet 27')
neighbourhoodlist = [loc1, loc2]
insertlocations(neighbourhoodlist)
for neighbourhood in neighbourhoodlist:
print neighbourhood.locationid
session.close()
Move session.close() out of your function and do it after you're done using that session.
Ditch the session.flush(), it's not needed since you already commit the session when you add the objects.