How to wrap a column in a CAST operation - python

I have an MSSQL database with tables which I cannot change and only ever interact with it as read only (SELECT statements). I am using sqlalchemy. What I need to do is to automatically wrap specific columns in CAST() SQL operations for every query. I want to do this at a low level so my code never needs to think about the problem. The reason I am doing this is explained in this question.
My table is something like this:
from sqlalchemy import Column, Integer, Sequence
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class myTable(Base):
__tablename__ = u'mytable'
id = Column(Integer, Sequence('table_id_seq'), primary_key=True)
problem_field = Column(DECIMAL(12, 4), nullable=True)
I have been trying to use a TypeDecorator like this:
from sqlalchemy import Column, Integer, Sequence, types
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.sql.expression import cast
Base = declarative_base()
class CastToFloatType(types.TypeDecorator):
'''Converts stored Decimal values to Floats via CAST operation
'''
impl = types.Numeric
def column_expression(self, col):
return cast(col, Float)
class myTable(Base):
__tablename__ = u'mytable'
id = Column(Integer, Sequence('table_id_seq'), primary_key=True)
wrapped_field = Column(CastToFloatType, nullable=True)
But it doesn't seem to do anything.

I think you need to make sure you're on at least version 0.8 of SQLAlchemy, where the column_expression() feature was added. A simple test of your code works on this end:
from sqlalchemy import Column, Integer, Sequence, types, Float
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.sql.expression import cast
Base = declarative_base()
class CastToFloatType(types.TypeDecorator):
'''Converts stored Decimal values to Floats via CAST operation
'''
impl = types.Numeric
def column_expression(self, col):
return cast(col, Float)
class myTable(Base):
__tablename__ = u'mytable'
id = Column(Integer, Sequence('table_id_seq'), primary_key=True)
wrapped_field = Column(CastToFloatType, nullable=True)
from sqlalchemy.orm import Session
s = Session()
print s.query(myTable)
output:
SELECT mytable.id AS mytable_id, CAST(mytable.wrapped_field AS FLOAT) AS mytable_wrapped_field
FROM mytable

Related

etl-process: from python-dataframe to postgres with SQLAlchemy

I want to create tables in a Postgres database using Python's SQLAlchemy package and insert data from a dataframe into them. I also want to assign foreign keys and primary keys.
The following code creates the two tables, but in the schema "public" instead of the schema "my_schema".
Can anyone find the error?
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey, MetaData
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
# Basisklasse für alle Tabellen definieren
metadata_obj = MetaData(schema="mein_schema")
Base = declarative_base(metadata_obj)
# Tabelle 2 definieren
class Tabelle2(Base):
__tablename__ = 'tabelle2'
id = Column(Integer, primary_key=True)
name = Column(String)
# Tabelle 1 definieren
class Tabelle1(Base):
__tablename__ = 'tabelle1'
id = Column(Integer, primary_key=True)
name = Column(String)
tabelle2_id = Column(Integer, ForeignKey('tabelle2.id'))
tabelle2 = relationship(Tabelle2)
# Alle Tabellen in der Datenbank erstellen
Base.metadata.create_all(engine)
You are passing metadata_obj as a parameter to declarative_base but not specifying it belongs to the metadata named parameter:
Base = declarative_base(metadata=metadata_obj)
declarative_base never knows the use the specified schema and falls back to the default schema of the connection.

How to define a function(expression dependant on other columns) as a default value to a column while defining table in SQLAlchemy?

How to add a function/expression which takes arguments as other columns as a default value to a column in the table of SQLAlchemy? For example: I want to define c as a column which is 2*x(other column);which should be saved in the database(could be in other table too). Can #hybrid_property decorator be used in this context?
from sqlalchemy import Column, Integer
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session, aliased
from sqlalchemy.ext.hybrid import hybrid_property, hybrid_method
from sqlalchemy import create_engine
from sqlalchemy import MetaData
from sqlalchemy.orm import sessionmaker
engine = create_engine('sqlite:///Helloworld.db', echo=False)
Session = sessionmaker(bind=engine)
metadata = MetaData(engine)
Base = declarative_base()
class HelloWorld(Base):
__tablename__ = 'helloworld'
pm_key = Column(Integer, primary_key=True)
x = Column(Integer, nullable=False)
c = Column(Integer,default=2*x)
Base.metadata.create_all(engine)
It is possible. Below I'am just adding a piece of code you can try . For more I think this will help you.
def mydefault(context):
return context.current_parameters.get('X')
class HelloWorld(Base):
__tablename__ = 'helloworld'
pm_key = Column(Integer, primary_key=True)
x = Column(Integer, nullable=False)
c = Column(Integer,default=mydefault)

Instancing a Table from __tablename__ in SQLAlchemy

Is it possible to instance a table with its tablename?
I've looked for in SQLAlchemy documentation and I couldn't find anything.
class A():
__tablename__ = 'x'
newTable = Table('x')
Is possible something like this?
This is a pseudo-language, not real Python code
Thanks,
Create_a.py
import os
import sys
from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy import create_engine
Base = declarative_base()
class A(Base):
__tablename__ = 'X'
A_id = Column(Integer, primary_key=True)
A_name = Column(String(250), nullable=False)
engine = create_engine('sqlite:///sqlalchemy_example.db')
Base.metadata.create_all(engine)
Insert_a.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from Create_a import A, Base, engine
Base.metadata.bind = engine
DBSession = sessionmaker(bind=engine)
session = DBSession()
print A.__tablename__
A.__tablename__ = A
new_A = A.__tablename__(A_name='new A')
session.add(new_A)
session.commit()
Were A.__tablename__ is X
Given some model Foo:
class Foo(Base):
__tablename__ = 'foos'
...
the associated table object can be accessed directly via the __table__ attribute (provided that the entity has been mapped, for example via Base.metadata.create_all):
tbl = Foo.__table__
If only the value of Foo.__tablename__ is available the table can be retrieved using reflection:
tbl = sa.Table('foos', sa.MetaData(), autoload_with=engine)

sqlalchemy, hybrid property case statement

This is the query I'm trying to produce through sqlalchemy
SELECT "order".id AS "id",
"order".created_at AS "created_at",
"order".updated_at AS "updated_at",
CASE
WHEN box.order_id IS NULL THEN "special"
ELSE "regular" AS "type"
FROM "order" LEFT OUTER JOIN box ON "order".id = box.order_id
Following sqlalchemy's documentation, I tried to achieve this using hybrid_property. This is what I have so far, and I'm not getting the right statement. It is not generating the case statement properly.
from sqlalchemy import (Integer, String, DateTime, ForeignKey, select, Column, create_engine)
from sqlalchemy.orm import relationship, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.hybrid import hybrid_property
Base = declarative_base()
class Order(Base):
__tablename__ = 'order'
id = Column(Integer, primary_key=True)
created_at = Column(DateTime)
updated_at = Column(DateTime)
order_type = relationship("Box", backref='order')
#hybrid_property
def type(self):
if not self.order_type:
return 'regular'
else:
return 'special'
class Box(Base):
__tablename__ = 'box'
id = Column(Integer, primary_key=True)
monthly_id = Column(Integer)
order_id = Column(Integer, ForeignKey('order.id'))
stmt = select([Order.id, Order.created_at, Order.updated_at, Order.type]).\
select_from(Order.__table__.outerjoin(Box.__table__))
print(str(stmt))
The hybrid property must contain two parts for non-trivial expressions: a Python getter and a SQL expression. In this case, the Python side will be an if statement and the SQL side will be a case expression.
from sqlalchemy import case
from sqlalchemy.ext.hybrid import hybrid_property
#hybrid_property
def type(self):
return 'special' if self.order_type else 'regular'
#type.expression
def type(cls):
return case({True: 'special', False: 'regular'}, cls.order_type)

How to automatically add a SQLAlchemy object to the session?

I have a SQLAlchemy table class created using the Declarative method:
mysqlengine = create_engine(dsn)
session = scoped_session(sessionmaker(bind=mysqlengine))
Base = declarative_base()
Base.metadata.bind = mysqlengine
class MyTable(Base):
__table_args__ = {'autoload' : True}
Now, when using this table within the code I would like to not have to use the session.add method in order to add each new record to the active session so instead of:
row = MyTable(1, 2, 3)
session.add(row)
session.commit()
I would like to have:
row = MyTable(1, 2, 3)
session.commit()
Now, I know of this question already: Possible to add an object to SQLAlchemy session without explicit session.add()?
And, I realize you can force this behavior by doing the following:
class MyTable(Base):
def __init__(self, *args, **kw):
super(MyTable, self).__init__(*args, **kw)
session.add(self)
However, I do not want to bloat my code containing 30 tables with this method. I also know that Elixir ( http://elixir.ematia.de/trac/wiki ) does this so it must be possible in some sense.
Super simple. Use an event:
from sqlalchemy import event, Integer, Column, String
from sqlalchemy.orm import scoped_session, sessionmaker, mapper
from sqlalchemy.ext.declarative import declarative_base
Session = scoped_session(sessionmaker())
#event.listens_for(mapper, 'init')
def auto_add(target, args, kwargs):
Session.add(target)
Base = declarative_base()
class A(Base):
__tablename__ = "a"
id = Column(Integer, primary_key=True)
data = Column(String)
a1 = A(data="foo")
assert a1 in Session()

Categories