On Thu, Aug 17, 2017 at 12:35 AM, Neena Parikh <ne...@benchling.com> wrote: > Hi there! > > I'm looking to create a helper or decorator function that will enable us to > "mark" a column or table as “unused”, and raise an error if that column or > table is queried for in SQL. > > > Context > The motivation behind this is to help us catch accidental references to > deleted columns in our code. We currently follow this process for database > migrations that involve dropping columns or tables: > > Remove all usages of the column in the code, except the column definition. > (We keep the column definition around since we have tests to ensure our > column definitions match up with our migrated database schemas.) > Make the column deferred > Wait a few days, then remove the column definition and run the database > migration that actually drops the column > > The main issue with this process is that if there’s any code left in that > loads in the deleted column, we run into a number of issues upon running the > migration. We’d like to introduce a helper or decorator for dropped > columns/tables that will trigger an error when the column/table is > referenced / loaded in any of the following ways: > > session.query(MyTable.my_column).all() > session.query(MyTable).options(undefer('*')).all() > my_table_instance.my_column > session.query(MyDroppedTable).all() > > Ideally, this would be a lightweight wrapper or decorator that could be > applied to any column or table — something like my_column = > unused(Column(…)) or an @unused decorator on a class. > > > Prior Work > We’ve looked at using hybrid_property to achieve this: > > > class MyTable(): > ... > _my_column = deferred(Column('my_column', String(255), nullable=True)) > > @hybrid_property > def my_column(self): > raise Exception('Unused column') > > @my_column.setter > def my_column(self, value): > raise Exception('Unused column') > > > What this gets us: > > Exception on my_table_instance.my_column > Exception on session.query(MyTable.my_column).all() > Exception on session.query(MyTable).options(undefer('my_column')).all() > > Issues with this approach: > > No exception on session.query(MyTable).options(undefer('*')).all() > Requires a fair amount of implementation overhead for each column > Not applicable to unused tables > > > > Any tips on how we can best use SQLAlchemy to accomplish all of the above? >
To catch the "undefer('*')" case, you could perhaps change the column type to something based on a TypeDecorator that raises an exception in process_bind_param and process_result_value: http://docs.sqlalchemy.org/en/latest/core/custom_types.html#sqlalchemy.types.TypeDecorator By "implementation overhead", do you mean the amount of typing you have to do each time you disable a column (creating the hybrid properties)? If so, you might be able to use the events system to create those properties automatically. For example, you could try listening for the "after_configured" mapper event, then inspect the class's properties. If you find any disabled columns, you could then attach corresponding hybrid properties that raise exceptions. Hope that helps, Simon -- SQLAlchemy - The Python SQL Toolkit and Object Relational Mapper http://www.sqlalchemy.org/ To post example code, please provide an MCVE: Minimal, Complete, and Verifiable Example. See http://stackoverflow.com/help/mcve for a full description. --- You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To unsubscribe from this group and stop receiving emails from it, send an email to sqlalchemy+unsubscr...@googlegroups.com. To post to this group, send email to sqlalchemy@googlegroups.com. Visit this group at https://groups.google.com/group/sqlalchemy. For more options, visit https://groups.google.com/d/optout.