Using sum() and unnest() with sqlalchemy - python

I'm trying to figure out proper syntax for using sum and unnest, but I have yet to figure out the proper syntax as currently generated SQL is invalid for PostgreSQL:
sqlalchemy.exc.ProgrammingError: (ProgrammingError) could not determine polymorphic type because input has type "unknown"
'SELECT (SELECT sum(foo) AS sum_1 \nFROM unnest(%(unnest_1)s) AS foo) AS anon_1 \nFROM products' {'unnest_1': 'stock'}
Working SQL would be:
SELECT (SELECT sum(foo) AS sum_1 FROM unnest(stock) AS foo) AS anon_1 FROM products;
Example test script is here:
from sqlalchemy import Column, Integer, select
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.dialects.postgresql import ARRAY
from sqlalchemy import func as F
from sqlalchemy.sql import column
import logging
Base = declarative_base()
db = create_engine('postgresql://scott:tiger#localhost/')
# create a configured "Session" class
Session = sessionmaker(bind=db)
session = Session()
logging.basicConfig()
class P(Base):
__tablename__ = 'products'
id = Column(Integer, primary_key=True)
stock = Column(ARRAY(Integer, dimensions=1))
total = Column(Integer)
def __init__(self, stock=[]):
self.stock = stock
self.total = 0
if __name__ == '__main__':
Base.metadata.create_all(db)
session.add_all([P([1, 2]), P([0, 0]), P([0, 2])])
session.commit()
_q = select([F.sum(column('foo'))]) \
.select_from(F.unnest('stock').alias('foo')).as_scalar()
q = select([_q]).select_from(P.__table__)
print session.execute(q).fetchall()

stock needs to be a column:
F.unnest(P.stock)
You passed a string bind argument.

Related

SQLAlchemy - How to select cerain rows?

How to simulate a SELECT in SQLAlchemy? I would like to create a function which takes a couple of parameters and returns a row which contains those values but I can't do SELECT.
The only way I found is below but I can't find metadata in SQLAlchemy module.
EDIT: I figured out that BoundMetaData is deprecated so MetaData is appropriate, but it says that Select has no len
# -*- coding: utf-8 -*-
import sqlalchemy
from sqlalchemy import Column, Table
from sqlalchemy import UniqueConstraint
from sqlalchemy import create_engine
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine('sqlite:///db.db', echo=False)
Base = declarative_base()
s = sqlalchemy.orm.Session(engine)
class Flight(Base):
__tablename__ = 'flights'
id = Column(sqlalchemy.Integer, primary_key=True)
destination_from = Column(sqlalchemy.String)
destination_to = Column(sqlalchemy.String)
creation_date = Column(sqlalchemy.Date)
start_date = Column(sqlalchemy.Date)
return_date = Column(sqlalchemy.Date)
price = Column(sqlalchemy.Float)
filename = Column(sqlalchemy.String)
bought_days_before = Column(sqlalchemy.Integer)
__table_args__ = (
UniqueConstraint('creation_date', 'destination_from', 'destination_to', 'start_date', 'return_date', 'price'),
)
Base.metadata.create_all(engine)
def insert_into_flights(**kwargs):
s.add(Flight(**kwargs))
try:
s.commit()
except IntegrityError as e:
s.rollback()
def get_prices(date_from, days, bought_days_before, destination, min=True, avg=False):
flights = Table('flights', metadata ,autoload=True
)
print len(flights.select())
s.query(Flight).filter(Flight.id==34).all()
This is an example selecting the Flight with id 34.
See SQLAlchemy docs

SQLalchemy cannot find my table

I am trying to follow the official SQLalchemy tutorial, but less than half way through it blows up in my face. I am using the following code:
from sqlalchemy import create_engine
engine = create_engine('sqlite:///:memory:', echo=True)
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind=engine, autoflush=False)
session = Session()
from sqlalchemy import Column, Integer, String, Sequence
class Substance(Base):
__tablename__ = 'substances'
id = Column(Integer, Sequence("substance_id_seq"), primary_key=True)
name = Column(String)
fullname = Column(String)
hazards = Column(String)
def __repr__(self):
return "<Substance(name='%s', fullname='%s', hazards='%s')>" % (self.name, self.fullname, self.hazards)
edta = Substance(name="EDTA", fullname="Ethylenediaminetetraacetic acid", hazards="lala")
session.add(edta)
subs = session.query(Substance).filter_by(name='EDTA').first()
print subs
This fails with:
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: substances [SQL: u'SELECT substances.id AS substances_id, substances.name AS substances_name, substances.fullname AS substances_fullname, substances.hazards AS substances_hazards \nFROM substances \nWHERE substances.name = ?\n LIMIT ? OFFSET ?'] [parameters: ('EDTA', 1, 0)]
Any ideas why or what I can do about it?
Please add Base.metadata.create_all(engine) to your code after all the classes are declared.
This creates all the tables you have declared through objects.
Also, another thing, use session.commit() to commit all the data you added through session.add(obj)

Get last inserted record's Primary Key in "declarative_base()"

I wanna to get Primary Key of last inserted, I already know two way for this :
1) "lastrowid" with "raw SQL"
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String, text
engine = create_engine('sqlite://')
meta = MetaData()
tbl = Table('tbl', meta,
Column('f1', Integer, primary_key=True),
Column('f2', String(64))
)
tbl.create(engine)
sql = text("INSERT INTO tbl VALUES (NULL, 'some_data')")
res = engine.execute(sql)
print(res.lastrowid)
2) "inserted_primary_key" with "insert()"
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String
engine = create_engine('sqlite://')
meta = MetaData()
tbl = Table('tbl', meta,
Column('f1', Integer, primary_key=True),
Column('f2', String(64))
)
tbl.create(engine)
ins = tbl.insert().values(f2='some_data')
res = engine.execute(ins)
print(res.inserted_primary_key)
but my problem is "declarative_base()"
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
engine = create_engine('sqlite://')
Base = declarative_base()
Session = sessionmaker(bind=engine)
session = Session()
class TBL(Base):
__tablename__ = 'tbl'
f1 = Column(Integer, primary_key=True)
f2 = Column(String(64))
Base.metadata.create_all(engine)
rcd = TBL(f2='some_data')
session.add(rcd)
session.commit()
If i do this:
res = session.add(rcd)
It give me "None". or if i do this:
res = session.commit()
same thing happend. My question is:
Is there any good way to access "lastrowid" or "inserted_primary_key" in case of "declarative_base()"?
What is the best approach ?
After calling session.commit(), accessing rcd.f1 will return its generated primary key. SQLAlchemy automatically reloads the object from database after it has been expired by the commit.

How can I create column_properties that use a groupby?

I have this sql query:
select
rooms.*,
COUNT(DISTINCT(o.resident_id)) as resident_count,
COUNT(reviews.id) as review_count,
COUNT(photos.id) as photo_count,
AVG(reviews.rating) as mean_review
from
t_rooms rooms
JOIN
t_room_listings listings on listings.room_id = rooms.id
JOIN
t_occupancies o on o.listing_id = listings.id
LEFT JOIN
t_reviews reviews on reviews.occupancy_id = o.id
LEFT JOIN
t_photos photos on photos.occupancy_id = o.id
GROUP BY rooms.id
Which I know I can write in ORM query form as:
q = (session
.query(
Room,
func.count(func.distinct(Occupancy.resident_id)).label('resident_count'),
func.count(Review.id).label('review_count'),
func.count(Photo.id).label('photo_count'),
(
(3 + func.avg(Review.rating)) / (1 + func.count(Review.rating))
).label('bayesian_rating')
)
.select_from(
join(Room, RoomListing).join(Occupancy).outerjoin(Review).outerjoin(Photo)
)
.group_by(Room.id)
)
for room, res_ct, rev_ct, p_ct in q:
wish_that_I_could_write(room.res_ct, room.rev_ct, room.p_ct, room.score)
But how can I declare resident_count, review_count etc as column_propertys in my Room class, so that I don't need to construct this query each time?
You may achieve this result with mapping query to object like so:
class ExtendedRoom(object):
pass
# q is your query
mapper(ExtendedRoom, q.statement.alias())
for room in session.query(ExtendedRoom).all():
# now room have review_count and other attributes
print(room.review_count)
Here simplified example with column_property.
from sqlalchemy import create_engine, Column, Integer, MetaData, Table, String, func
from sqlalchemy.sql import select
from sqlalchemy.orm import sessionmaker, mapper, column_property
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine('sqlite:///:memory:', echo=True)
Session = sessionmaker(bind=engine)
Base = declarative_base()
session = Session()
metadata = MetaData()
room = Table('room', metadata,
Column('id', Integer, primary_key=True),
Column('name', String),
Column('num', Integer, default=0),
)
metadata.create_all(engine)
statement = select([room]).group_by(room.c.id).alias()
class Room(object):
pass
mapper(Room, statement, properties={
'count': column_property(func.count(statement.c.num)),
})
print(session.query(Room).all())

Why is this column becoming NULL when I simply print it?

I have a table with a primary key of 'pk'. I am using SQLAlchemy's merge to update a row in my table:
my_obj = MyTable()
my_obj.pk = 1234
#print my_obj.col1
DBSession.merge(my_obj)
transaction.commit()
I alreay have a row w/ pk of 1234. If I run the code as is the row doesn't change but if I uncomment the line where I print my_obj.col1 then col1 for that row in my DB will become NULL for that column but all other columns for that row don't change. Is this a bug in sqlalchemy?
EDIT: Here's my class for MyTable():
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import scoped_session, sessionmaker
from zope.sqlalchemy import ZopeTransactionExtension
import transaction
from sqlalchemy import create_engine
DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
engine = create_engine('mysql://myhost,etc here',echo=False,pool_recycle=3600*5)
Base = declarative_base()
Base.metadata.bind = engine
class MyTable(Base):
__tablename__ = 'my_table'
__table_args__ = {'autoload':True}

Categories

Resources