Python Flask-SQLAlchemy inheritance

Viewed 1335

I'm trying to make an insert query but I'm getting this error:

sqlalchemy.orm.exc.FlushError: Attempting to flush an item of type as a member of collection "Tag.item". Expected an object of type or a polymorphic subclass of this type. If is a subclass of , configure mapper "Mapper|Item|items" to load this subtype polymorphically, or set enable_typechecks=False to allow any subtype to be accepted for flush.

The insert query

        project = Proyecto.query.filter_by(id_project=id_project).first()
        creado_en = datetime.datetime.utcnow()
        nuevo_hub = Hub(name,location,comments,creado_en)
        #FAIL HERE, even if Hub extends of Item
        nuevo_tag = Tag(project,nuevo_hub,TYPE_HUB,creado_en)
        db.session.add(nuevo_tag)
        db.session.add(nuevo_hub)
        db.session.commit()

The rest of code

class Item(db.Model):
    __tablename__ = "items"
    id_item     = db.Column(db.Integer, autoincrement=True, primary_key=True)
    type        = db.Column(db.Integer)
    created_at  = db.Column(db.DateTime)
    updated_at  = db.Column(db.DateTime)
    __mapper_args__ = {
        'polymorphic_identity': 'items',
        'polymorphic_on':type,
        'with_polymorphic':'*'
    }

    def __init__(self,creado_en=None):
        self.created_at = creado_en
        self.updated_at = creado_en



class Hub(Item):
    __tablename__ = "hubs"
    __mapper_args__ = {
        'polymorphic_identity': 0,
        'with_polymorphic':'*'
    }
    id_hub = db.Column(db.Integer, db.ForeignKey('items.id_item'), primary_key=True)
    # id_hub = db.Column(db.Integer, autoincrement=True, primary_key=True)
    name = db.Column(db.String(50), nullable=False, index= True)
    location = db.Column(db.String(50))
    comments = db.Column(db.String(128))
    created_at = db.Column(db.DateTime)
    updated_at = db.Column(db.DateTime)
    # conexiones = db.Column(db.Integer)

    def __init__(self, nombre=None, location=None,comments=None, creado_en=None):
        self.name = nombre
        self.location = location
        self.comments = comments
        self.created_at = creado_en
        self.updated_at = creado_en



class Tag(db.Model):
    __tablename__ = "tags"
    id_tag      = db.Column(db.Integer, autoincrement=True, primary_key=True)
    id_project  = db.Column(db.Integer,db.ForeignKey("projects.id_project"))
    id_item     = db.Column(db.Integer,db.ForeignKey("items.id_item"))
    project     = db.relationship(Proyecto, backref=db.backref('list_tags', lazy='dynamic'))
    item        = db.relationship(Item, backref=db.backref('list_tags', lazy='dynamic'))
    type        = db.Column(db.Integer) #(0,hub);(1,cable);(2,pipe);(3,electrical_pipes)
    created_at  = db.Column(db.DateTime)
    updated_at  = db.Column(db.DateTime)

    def __init__(self,project,item,type,created_at):
        self.project = project
        self.item = item
        self.type = type
        self.created_at = created_at
        self.updated_at = created_at
1 Answers

I had the same problem, in my case I fix that based on this answer: Flask-SQLAlchemy polymorphic association

Also, the official SQLAlchemy docs can help:

Joined Table Inheritance In joined table inheritance, each class along a particular classes’ list of parents is represented by a unique table. The total set of attributes for a particular instance is represented as a join along all tables in its inheritance path. Here, we first define the Employee class. This table will contain a primary key column (or columns), and a column for each attribute that’s represented by Employee. In this case it’s just name:

class Employee(Base):
    __tablename__ = 'employee'
    id = Column(Integer, primary_key=True)
    name = Column(String(50))
    type = Column(String(50))

    __mapper_args__ = {
        'polymorphic_identity':'employee',
        'polymorphic_on':type
    }

I think your code should look like:

class Hub(Item):
    __tablename__ = "hubs"
    
    id_hub = db.Column(db.Integer, autoincrement=True, primary_key=True)
    name = db.Column(db.String(50), nullable=False, index= True)
    location = db.Column(db.String(50))
    comments = db.Column(db.String(128))
    created_at = db.Column(db.DateTime)
    updated_at = db.Column(db.DateTime)
    # conexiones = db.Column(db.Integer)

    __mapper_args__ = {
        'polymorphic_identity': 0,
        'polymorphic_on': id_hub
    }

    def __init__(self, nombre=None, location=None,comments=None, creado_en=None):
        self.name = nombre
        self.location = location
        self.comments = comments
        self.created_at = creado_en
        self.updated_at = creado_en

You may also need to set is to concrete = True to be able to query it from child class.

Concrete inheritance is inheritance of method implementations and member variables from a super-class.

Details at: Abstract Concrete Classes

__mapper_args__ = {
    'polymorphic_identity': 0,
    'polymorphic_on': id_hub,
    'concrete': True
}
Related