Sqlalchemy return column value with map func - python

class MyClass(Base):
__tablename__ = 'my_table'
id = Column(Integer, primary_key=True)
type_id = Column(String(50))
# map value
type_name = {
"type_id_1":"name_1",
"type_id_2":"name_2"
}
Is there a way to return "type name" when doing query type_id from table Myclass ?
by using #hybrid_property, I got really close to the goal
class Myclass(Base):
...
#hybrid_property
def type_name(self):
# The goal is get type_name, but it's not working since self.type_id is not a String object
name = type_name[self.type_id]
return cast(name, String)

reading your question I interpreted it this way. Do you want this?
class MyClass(Base):
__tablename__ = 'my_table'
id = Column(Integer, primary_key=True)
type_id = Column(String(50))
def __repr__(self):
return "<my_table(id='%s', type_id='%s')>" % (self.id, self.type_id)

Related

How can I access subclasses from upper class in sqlalchemy?

I have 3 classes;
'Company' top class its subclass 'Department' its subclass 'DepartmentalUnit'
I can access the values ​​of all classes from the 'DepartmentalUnit' class to the top class 'Company'
What I could not do and understand despite reading the document is that;;
I cannot access other subclasses from the 'company' class
class Company(Base):
__tablename__ = 'company'
id = Column(Integer, primary_key=True)
name = Column(String)
departments = relationship('Department',backref='company')
class Department(Base):
__tablename__ = 'department'
id = Column(Integer, primary_key=True)
department_name = Column(String)
company_id = Column(Integer, ForeignKey('company.id'))
departmentalunits = relationship('DepartmentalUnit', backref='department')
class DepartmentalUnit(Base):
__tablename__ = 'departmentalunit'
id = Column(Integer, primary_key=True,nullable=False)
departmental_unit_name = Column(String)
departments_id = Column(Integer, ForeignKey('department.id'))
The code from which I access the upper classes from the subclasses:
query = session.query(DepartmentalUnit)
instance = query.all()
for i in instance:
print(i.department.company.name)
print(i.department.department_name)
print(i.departmental_unit_name)
The code I can't access other subclasses from the company class:
query = session.query(Company)
instance = query.all()
for i in instance:
print(i.department.department_name)
Your last query should be used differently:
there is a typo in the name of the relationship: should be departments instead of department
given that the relationship is 1-N, the result is a list, so you should iterate over children.
This should work:
query = session.query(Company)
for company in query.all():
print(company.name)
for dep in company.departments:
print(" ", dep.department_name)
for dep_unit in dep.departmentalunits:
print(" ", dep_unit.departmental_unit_name)
I solved the problem. I added a backref to relationships and now I can access all of them from the company. Not sure if it's a correct method? However, I am currently getting the return I want. I have no unanswered request yet.
Example solved:
class Company(Base):
__tablename__ = 'company'
id = Column(Integer, primary_key=True)
name = Column(String)
departments = relationship('Department',backref='company',uselist=False)
class Department(Base):
__tablename__ = 'department'
id = Column(Integer, primary_key=True)
department_name = Column(String)
company_id = Column(Integer, ForeignKey('company.id'))
departmentalunits = relationship('DepartmentalUnit', backref='department',uselist=False)
class DepartmentalUnit(Base):
__tablename__ = 'departmentalunit'
id = Column(Integer, primary_key=True,nullable=False)
departmental_unit_name = Column(String)
departments_id = Column(Integer, ForeignKey('department.id'))
query = session.query(Company)
instance = query.all()
for i in instance:
print(f"Company: {i.name}")
print(f"Department: {i.departments.department_name}")
print(f"Department Unit: {i.departments.departmentalunits.departmental_unit_name}")
print( f"Report Category : {i.departments.departmentalunits.reportcategoryoftheunit.report_category_name}")

TypeError: unhashable type: 'list' error with the sqlalchemy insert query

I am trying to insert a record into a oracle db via sqlalchemy, here is my below model:
class Item(Base):
__tablename__ = 'items'
id = db.Column(db.Integer, db.Sequence('item_id_seq'), nullable=False, primary_key=True)
name = db.Column(db.String(128), nullable=True)
def __init__(self, name):
self.name = name
I have already created the table in the db by running the db migrate and upgrade. I am trying to insert a simple row in the db table as in :
test_obj = Item(name='item-1')
db.session.add(test_obj)
db.session.commit()
But the insert fails with the error TypeError: unhashable type: 'list'
But if I insert it along with the id (as below) and with the id initialized in the constructor, it inserts successfully.
test_obj = Item(id = 1, name='item-1')
db.session.add(test_obj)
db.session.commit()
class Item(Base):
...
def __init__(self, id, name):
self.id = id
self.name = name
Since I have set id with a sequence, it should auto increment and get inserted automatically, but this is not the case here, any help would be appreciated.
USE sqlalchemy 1.3.6 or above
Sqlalchemy does not support auto increment for oracle 11g
if you are using "oracle 11g" then use following code:
from sqlalchemy import event
from sqlalchemy.schema import CreateSequence, DDL
db_session = sessionmaker(bind={your database engine})
class Item(Base):
__tablename__ = 'items'
id = db.Column(db.Integer, nullable=False, primary_key=True)
name = db.Column(db.String(128), nullable=True)
def __init__(self, name):
self.name = name
def create_trigger_for_items(*args, **kwargs):
with con.db_session() as session:
session.execute(CreateSequence(db.Sequence('item_id_seq')))
session.execute(DDL("""CREATE OR REPLACE TRIGGER ITEM_ID_TRIGGER
BEFORE INSERT ON items
REFERENCING NEW AS NEW
FOR EACH ROW
BEGIN
:NEW.id := item_id_seq.NEXTVAL;
END;"""))
event.listen(Item.__table__, "after_create",
create_trigger_for_items)
if you are using "oracle 12" then use following code:
class Item(Base):
__tablename__ = 'items'
id = db.Column(db.Integer, db.Sequence('item_id_seq'), default=db.Sequence('item_id_seq').next_value(), primary_key=True)
name = db.Column(db.String(128), nullable=True)
def __init__(self, name):
self.name = name
if you are using "oracle 12c" then use following code:
class Item(Base):
__tablename__ = 'items'
id = db.Column(db.Integer, autoincrement=True, primary_key=True)
name = db.Column(db.String(128), nullable=True)
def __init__(self, name):
self.name = name
then you can use:
test_obj = Item(name='item-1')
db.session.add(test_obj)
db.session.commit()
Just mark id as the primary_key.
class Item(Base):
__tablename__ = 'items'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(128), nullable=True)
def __init__(self, name):
self.name = name

SQLAlchemy, concise ways of defining many similar classes/tables

So I have a lot of tables with a general stucture of
Base = declarative_base()
class Thing(Base):
__tablename__ = 'thing'
uid = Column(Integer, Sequence('Thing_id_seq'), primary_key=True)
name = Column(String)
def __repr__(self):
return "something"
class ThingEntry(Base):
__tablename__ = 'thingentry'
uid = Column(Integer, Sequence('ThingEntry_id_seq'), primary_key=True)
foo = Column(Integer, ForeignKey('foo.uid'))
entity = Column(Integer, ForeignKey('thing'))
class Quu(Base):
__tablename__ = 'quu'
uid = Column(Integer, Sequence('Quu_id_seq'), primary_key=True)
name = Column(String)
description = Column(String)
def __repr__(self):
return "something"
class QuuEntry(Base):
__tablename__ = 'quuentry'
uid = Column(Integer, Sequence('QuuEntry_id_seq'), primary_key=True)
foo = Column(Integer, ForeignKey('foo.uid'))
entity = Column(Integer, ForeignKey('quu'))
What are some more concise ways of defining all these classes/tables? This method has a lot of code duplication/self-repeating.
I was thinking of some kind of inheritance so that I could bring that code down to
class Thing(Base):
pass
class ThingEntry(Base):
pass
class Quu(Base):
description = Column(String)
class QuuEntry(Base):
pass
With some magic auto-assigning the other values (__tablename__, uid, foo, etc), but I'm not sure if that's possible or optimal.
You should look at documentation about auto reflection http://docs.sqlalchemy.org/en/rel_1_1/core/reflection.html
Used a factory approach with metaclasses, as such:
class ObjectFactory:
def __new__(cls, class_name, parents, attributes):
attributes['__tablename__'] = class_name
attributes['uid'] = Column(Integer, Sequence(class_name + '_id_seq'), primary_key=True)
attributes['name'] = Column(String)
class EntryFactory:
def __new__(cls, class_name, parents, attributes):
attributes['__tablename__'] = class_name
attributes['uid'] = Column(Integer, Sequence(class_name + '_id_seq'), primary_key=True)
attributes['foo'] = Column(Integer, ForeignKey('foo.uid'), nullable=False)
attributes['entity_id'] = Column(Integer, ForeignKey(class_name[:-5]), nullable=False)
class Thing(Base, metaclass=ObjectFactory):
pass
class ThingEntry(Base, metaclass=EntryFactory):
pass
class Quu(Base, metaclass=ObjectFactory):
description = Column(String)
class QuuEntry(Base, metaclass=EntryFactory):
pass

SQLAlchemy polymorphic update

I have a class Parent and two of its subclass Child and ChildOne.
I am able to add data to each of the table and read the data.
Now I want to update Child class's row such that it becomes a row in ChildOne table that is when I update type to "old" from "young", I want the row to get deleted from child_one table and added to child table, but without losing the "id" value (which is the primary key).
Is there any way that SQLAlchemy itself handle this? or any other idea to achieve this?
from sqlalchemy import create_engine, ForeignKey
from sqlalchemy import Column, Integer, String, case, Boolean
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base, declared_attr
engine = create_engine('sqlite:///testing.db', echo=True)
Base = declarative_base()
class Parent(Base):
__tablename__ = "parent"
id = Column(Integer, primary_key=True)
name = Column(String, unique=True)
type = Column(String)
def __init__(self, name):
self.name = name
self.type = type
__mapper_args__ = {
'polymorphic_identity':'parent',
'polymorphic_on': case(
[(type == "young", "child_one"),
(type == "old", "child")]
)
}
class Child(Parent):
__tablename__ = "child"
id = Column(Integer, ForeignKey('parent.id'), primary_key=True)
school = Column(String, default="some high school")
def __init__(self, name, school):
self.name = name
self.type = type
self.school = school
__mapper_args__ = {
'polymorphic_identity':'child'
}
class ChildOne(Parent):
__tablename__ = "child_one"
id = Column(Integer, ForeignKey('parent.id'), primary_key=True)
school = Column(String, default="UCLA")
mode = Column(Boolean, default=1)
bool = Column(Boolean, default=0)
def __init__(self, name, type, school, mode, bool):
self.name = name
self.type = type
self.school = school
self.mode = mode
self.bool = bool
__mapper_args__ = {
'polymorphic_identity':'child_one'
}
from sqlalchemy import event
# standard decorator style
#event.listens_for(Child, 'after_update')
def receive_after_update(mapper, connection, target):
"listen for the 'after_update' event"
if target.type == "young":
ChildOne(id=target.id)
But honestly, you should just be creating triggers in the database from something like this. It doesn't require overly complex sqlalchemy code.

Sqlalchemy declarative inheritance of __table_args__

Suppose I have such table
class Base(object):
id = Column(Integer, primary_key=True)
date_updated = Column(Date, nullable=True)
rating = Column(Integer, nullable=False, default=0)
status = Column(SmallInteger, nullable=False, default=0)
#declared_attr
def __tablename__(cls):
return "table_%s" % cls.LANG
#declared_attr
def __table_args__(cls):
return (
Index('ix__%s__rating' % cls.__tablename__, 'rating'),
Index(
'ix__%s__status' % cls.__tablename__,
'status',
postgresql_where=column('status') == 0,
),
Index(
'ix__%s__date_updated' % cls.__tablename__,
'date_updated',
postgresql_where=column('date_updated') != None,
),
)
class TableEn(Base, db.Model):
LANG = 'en'
class TableUk(Base, db.Model):
LANG = 'uk'
Some way I've found how to create such (partial) indexes as status and date_updated in parent __table_args__.
But I need to create desc sorting index rating, like func.desc(rating), but I do not know how to do that.
None of variants worked for me (variant and it's error):
Index('ix__%s__rating' % cls.__tablename__, 'rating desc')
KeyError: 'rating desc'
Index('ix__%s__rating' % cls.__tablename__, cls.rating.desc())
sqlalchemy.exc.ArgumentError: Can't add unnamed column to column collection
Index('ix__%s__rating' % cls.__tablename__, desc('rating'))
while creating schema in db
sqlalchemy.exc.ProgrammingError: (ProgrammingError) syntax error at or near ")"
LINE 1: CREATE INDEX ix__table_en__rating ON table_en ()
Of course I can create that index manually with direct SQL, but I'm sure solution exists somewhere.
Thanks in advance!
Here simplified code that doing what you want:
class BaseModel(Base):
__abstract__ = True
id = Column(Integer, primary_key=True)
rating = Column(Integer, nullable=False, default=0)
#declared_attr
def __tablename__(cls):
return "table_%s" % cls.LANG
#classmethod
def build_indexes(cls):
Index('ix__%s__rating' % cls.__tablename__, cls.__table__.c.rating.desc())
#event.listens_for(BaseModel, 'instrument_class', propagate=True)
def receive_mapper_configured(mapper, class_):
class_.build_indexes()
class TableEn(BaseModel):
LANG = 'en'
class TableUk(BaseModel):
LANG = 'uk'
Full code here.
Here you are having 2 problems. First, you should call desc against table column, not an mapper object attribute. But if you do it you fill have problem, because __table__ attribute is created after getting info from __table__args__. So you need create indexes after creating __table__ field. One way to do it is via sqlalchemy events.

Categories

Resources