pythonviewsqlalchemy

SQLAlchemy: is it safe to add a new column to a view, not mentioned in the model?


Suppose I have a view, for example

create view MyView as select 1 as A, 2 as B

(The above SQL is Microsoft dialect but the details of the view itself are not important.)

I wrap this view in my SQLAlchemy model definition:

class MyView(Base):
    __tablename__ = 'MyView'
    A = Column(Integer)
    B = Column(Integer)

The view isn't only used from within SQLAlchemy. Other applications or hand-written SQL queries may use it. Suppose I want to add a new column to the view, so it now reads

create view MyView as select 1 as A, 2 as B, 3 as C

(This new version of the view is backwards-compatible for most application queries. Only queries that use select * would see a different result. Queries that join the view with some other view or table also providing a column called C, but do not fully qualify column names, might get an error about ambiguous column names.)

Suppose for the moment I do not plan to use the column C from SQLAlchemy and I don't add it to the model definition. I may do so eventually, but for whatever reason I can't change the code just yet, or at least not release the new version. Is it nonetheless safe to add the column to the view and remain compatible with the existing SQLAlchemy model file?

Because I am asking about a view, not a table, please assume that SQLAlchemy is providing only read access. The application is not trying to update the view or delete from the view, even if that might be possible in some RDBMSes.

There won't be any problem if, say, SQLAlchemy tries to introspect the columns in the view and complains about additional ones not mentioned in the model? I think it doesn't... but I have not found a definitive answer.


Solution

  • This should be fine, as long as you explicitly define the columns that you want from the view in the mapping. So this (which you say you are using) is fine

    class MyModel(Base):
        __tablename__ = 'my_view'
        
        id = Column(Integer, primary_key=True)
        col = Column(Integer)
    

    because the mapper knows which columns are being mapped, and will only use those columns in query statements.

    This would not be so good:

    my_view = Table('my_view', Base.metadata, autoload_with=engine)
    
    class MyModel(Base):
        __table__ = my_view
    

    because the mapper would use all the columns in the reflected table.