Insert into temp table using SQLAlchemy - python

I want to create a temp table from a result of CTE using SQLAlchemy.
Tables definition:
class Data(Base):
__tablename__ = 'data'
c_id = Column(Integer, primary_key=True)
# ...
# temp table
class CIdTmp(Base):
__tablename__ = '#c_id_tmp'
c_id = Column(Integer, primary_key=True)
This is my CTE:
c_id_cte = (session.query(Data.c_id)).cte('c_id_cte')
I tried combining insert() with from_select() like this:
session.execute(CIdTmp.insert().from_select(['c_id'], c_id_cte))
But it produces this error:
AttributeError: type object 'CIdTmp' has no attribute 'insert'

Ok, I found out that I have to get a Table object to execute an insert expression. It can be done by using ___table__ on my model.
session.execute(CIdTmp.__table__.insert().from_select(['c_id'], c_id_cte))

Related

SqlAlchemy relationship selecting columns

I am trying to find a way to select columns with an sqlalchemy relationships:
I have this two tables:
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
label = Column(String)
children = relationship("Child", back_populates="parent")
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('public.parent.id'))
parent = relationship("Parent", back_populates="children")
when i use
db.query( models.Parent).first()
i get the parent object with the list of Children as i was expecting, but what i would like to do is to select only few columns like that:
db.query( models.Parent.id, models.Parent.children)
in this case it doesn't work an i get the following error:
Could not locate column in row for column Children
You can use options with load_only() function.
stmt = select(Parent)
results = await db.execute(
stmt
.options(load_only(Parent.id))
.options(selectinload(Parent.children))
)
*If you want picking some columns, just attach load_only() like this.
.options(selectinload(Parent.children).load_only(Child.id, Child.name))
Yes, I tested execute() but, query() will work.
session.query(User).options(load_only(User.name, User.fullname))
see also:
https://docs.sqlalchemy.org/en/14/orm/loading_columns.html#sqlalchemy.orm.load_only

Return the Table whose primary key is another table's foreign key, SQL Alchemy

I can return the name of the table of which a foreign key references, but I want to do the opposite.
Suppose I have two tables, Users and Address, and Address has a foreign key to Users (one-to-many). I also made this bidirectional so that I can get the User from the address table.
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String)
address = relationship("Address", back_populates="user")
class Address(Base):
__tablename__ = 'address'
id = Column(Integer, primary_key=True, autoincrement=True)
company_name = Column(String)
user_id = Column(Integer, ForeignKey('user.id'))
user = relationship("User", back_populates="address")
If I wanted to figure out the name of the parent table, in a way that works for any other table in a similar relationship, I can to this:
for i in Address.__table__.foreign_key_constraints:
print(i.referred_table)
tab = Table(Address.__tablename__, Base.metadata, autoload_with=engine, extend_existing=True)
for i in tab.foreign_key_constraints:
print(i.referred_table)
#both output "user" which is the __tablename__ of User
This was me accessing the User table using the foreign key constraint attribute. But how do I do the opposite, using an attribute from User to access Address? I mainly want to know so I can handle relationships in a bulk core insert.
tab = Table(User.__tablename__, Base.metadata, autoload_with=engine, extend_existing=True)
records = df.to_dict(orient="records")
insert_stmt = sqlalchemy.dialects.sqlite.insert(tab).values(records)
#list of primary keys
pks = [pk.name for pk in tab.primary_key]
update_columns = {col.name: col for col in insert_stmt.excluded if col.name not in pks}
update_statement = insert_stmt.on_conflict_do_update(index_elements=pks, set_=update_columns)
The above works once I execute the on_conflict_do_update statement, but it does not generate any Address rows.
I found one way of doing it, but it's not as clean as I wanted.
tab = list(inspect(User).relationships)[0].entity.local_table
For any declarative table (classes), you can inspect them to get a Mapper Object. The Mapper Object has relationships as an attribute, which returns a collection of relationships.
inspect(User).relationships
#Get the relationship, note this will return the relationship attribute from the class
list(inspect(User).relationships)[0]
So I then get the mapper/class this relationship belongs to with .entity. Then from the declarative table, a table object is returned with the local_table attribute
entity = list(inspect(User).relationships)[0].entity
tab = entity.local_table
This statement should evaluate to True
tab == Address.__table__

How should I test the result of an SQLAlchemy query?

Given the following table:
class Table(Base):
__tablename__ = "table"
id = Column(Integer, primary_key=True)
name = Column(String(50))
What is the best way of testing result from below:
result = session.query(Table)
Should I convert to a dictionary and compare against a dictionary fixture?

sqlalchemy orm skip already-existing tables on create

How can I avoid this exception in sqlalchemy orm when I try to create a table which already exists in a database:
sqlalchemy.exc.InvalidRequestError: Table 'col1' is already defined for this MetaData instance. Specify 'extend_existing=True' to redefine options and columns on an existing Table object.
Base = automap_base()
Base.prepare(engine, reflect=True)
class Col1(Base):
__tablename__ = 'col1'
id = Column(Integer(), primary_key=True)
name = Column(String())
Base.metadata.create_all(engine)
I needed to add {'useexisting': True} (in SQLAlchemy 1.4+, use {'extend_existing': True}).
class Col1(Base):
__tablename__ = 'col1'
__table_args__ = {'useexisting': True}
id = Column(Integer(), primary_key=True)
name = Column(String())

How to create relationships from existing relationships in SQLAlchemy?

I'm getting a bit stuck with relationships within relationships in sqlalchemy. I have a model like this:
engine = create_engine('sqlite:///tvdb.db', echo=True)
Base = declarative_base(bind=engine)
episodes_writers = Table('episodes_writers_assoc', Base.metadata,
Column('episode_id', Integer, ForeignKey('episodes.id')),
Column('writer_id', Integer, ForeignKey('writers.id')),
Column('series_id', Integer, ForeignKey('episodes.series_id'))
class Show(Base):
__tablename__ = 'shows'
id = Column(Integer, primary_key = True)
episodes = relationship('Episode', backref = 'shows')
class Episode(Base):
__tablename__ = 'episodes'
id = Column(Integer, primary_key = True)
series_id = Column(Integer, ForeignKey('shows.id'))
writers = relationship('Writer', secondary = 'episodes_writers',
backref = 'episodes')
class Writer(Base):
__tablename__ = 'writers'
id = Column(Integer, primary_key = True)
name = Column(Unicode)
So there is one-to-many between shows and episodes, and many-to-many between episodes and writers, but I now want to create a many-to-many relationship between shows and writers, based on the fact that a writer is associated with an episode of a show. I tried to add another column to the assoc table but that results in the following exception:
sqlalchemy.exc.ArgumentError: Could not determine join condition between parent/child tables on relationship Episode.writers. Specify a 'primaryjoin' expression. If 'secondary' is present, 'secondaryjoin' is needed as well.
So I'm obviously doing something wrong with my relationship declarations. How do I create the right primaryjoins to achieve what I'm trying to do here?
The fix is suggested right in the error message. Anyway, you have no need for a column that denormalises your schema; writer.shows can be an associationproxy.

Categories