Search code examples
pythonpostgresqlflasksqlalchemyflask-admin

SQLAlchemy mapping table columns with filters


I have a table in PostgreSQL that includes information about documents. Lets say something like that:

table: doc

id (int)
name (string)
type (int)

type is a category for a document (e.g. 1 - passport, 2 - insurance etc.). Also I have different tables with additional information for every docyment type.

table: info

id (int)
doc_id (fk)
info (additional columns)

I want to have a SQLAlchemy model to work with each type of document linked with it's additional information and be able to manage columns to display (for Flask-Admin if it is important).

Now to join two tables into some sort of "model" I used Mapping Table Columns from the SQLAlchemy documentation like that (when there was only one type of documents):

class DocMapping(db.Model):

    __table__ = doc.__table__.join(info)
    __mapper_args__ = {
        'primary_key': [doc.__table__.c.id]
    }

Now the question is that: how to create multiple classes inherited from db.Model (DocPassportMapping, DocInsuranceMapping etc.) based on doc.type column?

Something like that:

__table__ = doc.__table__.join(info).filter(doc.type)

That is obviously not working because we don't have a query object here.


Solution

  • If I understood you correctly, you wish to have an inheritance hierarchy based on DocMapping with DocMapping.type as the polymorphic identity. Since you have not provided a complete example, here is a somewhat similar structure. It has differences for sure, but should be applicable to yours. This uses single table inheritance on top of the joined mapping.

    The models:

    In [2]: class Doc(Base):
       ...:     id = Column(Integer, primary_key=True, autoincrement=True)
       ...:     name = Column(Unicode)
       ...:     type = Column(Integer, nullable=False)
       ...:     __tablename__ = 'doc'
       ...:     
    
    In [3]: class Info(Base):
       ...:     __tablename__ = 'info'
       ...:     doc_id = Column(Integer, ForeignKey('doc.id'), primary_key=True)
       ...:     value = Column(Unicode)
       ...:     doc = relationship('Doc', backref=backref('info', uselist=False))
       ...:     
    
    In [4]: class DocMapping(Base):
       ...:     __table__ = Doc.__table__.join(Info)
       ...:     __mapper_args__ = {
       ...:         'primary_key': (Doc.id, ),
       ...:         # These declare this mapping polymorphic
       ...:         'polymorphic_on': Doc.type,
       ...:         'polymorphic_identity': 0,
       ...:     }
       ...:     
    
    In [5]: class Passport(DocMapping):
       ...:     __mapper_args__ = {
       ...:         'polymorphic_identity': 1,
       ...:     }
       ...:     
    
    In [6]: class Insurance(DocMapping):
       ...:     __mapper_args__ = {
       ...:         'polymorphic_identity': 2,
       ...:     }
       ...:     
    

    Testing:

    In [7]: session.add(Insurance(name='Huono vakuutus',
       ...:                       value='0-vakuutus, mitään ei kata'))
    
    In [8]: session.commit()
    
    In [15]: session.query(DocMapping).all()
    Out[15]: [<__main__.Insurance at 0x7fdc0a086400>]
    
    In [16]: _[0].name, _[0].value
    Out[16]: ('Huono vakuutus', '0-vakuutus, mitään ei kata')
    

    The thing is: you probably do not want multiple classes that inherit from db.Model as base, but classes that inherit from DocMapping. It makes a lot more sense as a hierarchy.