Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get sqlalchemy base class object instead of children

I have three classes in my model, which one class inherited by the other two:

class Item(Base):
    __tablename__ = 'item'

    id = Column(Integer, primary_key=True)
    title = Column(Unicode(300))
    type = Column(Unicode(50))

    __mapper_args__ = {
        'polymorphic_on': type
    }


class Note(Item):
    __tablename__ = 'note'

    id = Column(Integer, ForeignKey('item.id'), primary_key=True)
    extra = Column(Text)

    __mapper_args__ = {
        'polymorphic_identity': 'note'
    }


class Task(Item):
    __tablename__ = 'task'

    id = Column(Integer, ForeignKey('item.id'), primary_key=True)
    another_extra = Column(Text)

    __mapper_args__ = {
        'polymorphic_identity': 'task'
    }

So, when I execute session.query(Item).all() I get a list that includes both Note and Task objects, but I don't want that, I want my objects to be the instance of Item class and just have id, title, type, not those extra fields. how should I write the query?

to clarify more, currently, I get:

[
     <models.Note object at 0x7f25ac3ffe80>,
     <models.Task object at 0x7f25ac3ffe80>,
     <models.Task object at 0x7f25ac3ffe80>,
     ...
]

But I want to get:

[
    <models.Item object at 0x000000000000>,
    <models.Item object at 0x000000000000>,
    <models.Item object at 0x000000000000>,
    ...
]
like image 794
Mehrdad Pedramfar Avatar asked Nov 24 '18 12:11

Mehrdad Pedramfar


1 Answers

NOTE: This may be problematic in a multi-threaded application.

You could use a context manager to temporarily block the polymorphism:

from contextlib import contextmanager
from sqlalchemy import inspect

@contextmanager
def no_poly(class_):
    mapper = inspect(class_).mapper
    polycol = mapper.polymorphic_on
    mapper.polymorphic_on = None
    yield class_
    mapper.polymorphic_on = polycol

Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
task = Task(title='Task Title', another_extra='something')
s = Session()
s.add(task)
s.commit()

# opening a new session as if the pk already exists in the 
# identity map it will return whatever type that pk is 
# pointing at.
s = Session() 
with no_poly(Item) as class_:
    inst = s.query(class_).all()
    print(inst)  # [<__main__.Item object at 0x000001443685DDD8>]
s = Session()  # new session again.
inst = s.query(Item).all()
print(inst)  #  [<__main__.Task object at 0x00000144368770B8>]

Something to be mindful of and as noted in the comments in my example, if the identity of the object is already referenced in the Identity Map, then you will get back whatever type is held in there, regardless of the class that you query on.

like image 175
SuperShoot Avatar answered Nov 01 '22 13:11

SuperShoot