Inserting objects and using ids in SqlAlchemy - python

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.

Related

SQLAlchemy (sqlite3.OperationalError) no such table:

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

SQLAlchemy: update & delete value from database

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.

How to commit another object in a property of an object in sqlalchemy?

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()

Instantiating object automatically adds to SQLAlchemy Session. Why?

From my understanding of SQLAlchemy, in order to add a model to a session, I need to call session.add(obj). However, for some reason, in my code, SQLAlchemy seems to do this automatically.
Why is it doing this, and how can I stop it? Am I approaching session in the correct way?
example
>>> from database import Session as db
>>> import clients
>>> from instances import Instance
>>> from uuid import uuid4
>>> len(db.query(Instance).all())
>>> 0 # Note, no instances in database/session
>>> i = Instance(str(uuid4()), clients.get_by_code('AAA001'), [str(uuid4())])
>>> len(db.query(Instance).all())
>>> 1 # Why?? I never called db.add(i)!
database.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy.ext.declarative import declarative_base
import config
Base = declarative_base()
class Database():
def __init__(self):
db_url = 'postgresql://{:s}:{:s}#{:s}:{}/{:s}'.format(
config.database['user'],
config.database['password'],
config.database['host'],
config.database['port'],
config.database['dbname']
)
self.engine = create_engine(db_url)
session_factory = sessionmaker(bind=self.engine)
self.session = scoped_session(session_factory)
Database = Database()
Session = Database.session
instance.py
from sqlalchemy import Column, Text, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.dialects.postgresql import UUID, ARRAY
import database
Base = database.Base
class Instance(Base):
__tablename__ = 'instances'
uuid = Column(UUID, primary_key=True)
client_code = Column(
Text, ForeignKey('clients.code', ondelete='CASCADE'), nullable=False)
mac_addresses = Column(ARRAY(Text, as_tuple=True),
primary_key=True)
client = relationship("Client", back_populates="instances")
def __init__(self, uuid, client, mac_addresses):
self.uuid = uuid
self.client = client
self.mac_addresses = tuple(mac_addresses)
client.py
from sqlalchemy import Column, Text
from sqlalchemy.orm import relationship
import database
from database import Session as db
Base = database.Base
class Client(Base):
__tablename__ = 'clients'
code = Column(Text, primary_key=True)
name = Column(Text)
instances = relationship("Instance", back_populates='client')
def __init__(self, code, name=None):
self.code = code
self.name = name
def get_by_code(code):
client = db.query(Client).filter(Client.code == code).first()
return client
When you create a SQLAlchemy object and link it directly to another SQLAlchemy object, both objects end up in the session.
The reason is that SQLAlchemy needs to make sure you can query these objects.
Take, for example, a user with addresses.
If you create a user in code, with an address, both the user and the address end up in the session, because the address is linked to the user and SQLAlchemy needs to make sure you can query all addresses of a user using user.addresses.all().
In that case all (possibly) existing addresses need to be fetched, as well as the new address you just added. For that purpose the newly added address needs to be saved in the database.
To prevent this from happening (for example if you only need objects to just calculate with), you can link the objects with their IDs/Foreign Keys:
address.user_id = user.user_id
However, if you do this, you won't be able to access the SQLAlchemy properties anymore. So user.addresses or address.user will no longer yield results.
The reverse is also true; I asked a question myself a while back why linking two objects by ID will not result in SQLAlchemy linking these objects in the ORM:
relevant stackoverflow question
another description of this behavior

Concurrent atomic select-update

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)

Categories