I'm writing SA classes against a very unnormalized legacy database.  Some of 
our columns comprise several distinct values, and I wanted to take this 
opportunity to split those into separate properties.  I also wanted to be able 
to search on the values in those properties, and here's my solution to it.

################################################
from sqlalchemy import func
from sqlalchemy.orm.properties import ColumnProperty

def substrcomparatormaker(column, i, j=None):
    """Return a comparator that tests for the substring of 'column'
    from 'i' to 'j', as specified with Python slice values.  This
    means setting the start column to i + 1 because Python is 0-based
    and SQL is 1-based, and setting the length to j - i.

    To search for columns where table.foo[0:3] has a certain value, use:

        @comparable_using(substrcomparatormaker(foo, 0, 3)) # => substr(1, 3)

    To search on table.foo[4:6], use:
        
        @comparable_using(substrcomparatormaker(foo, 4, 6)) # => substr(5, 2)
    """
    
    class SubstrComparator(ColumnProperty.Comparator):
        """Subclass of Comparator that looks at a slice of a column"""

        def __eq__(self, other):
            """Compare the substr of a column to the given value"""
            if j is None:
                return func.substr(column, i + 1) == other
            else:
                return func.substr(column, i + 1, j - i) == other

    return SubstrComparator
################################################

Here's how I use it in production:

class Invoice(Base):
    __tablename__ = 'invoice'
    typeofinv = Column(String(10))

    @comparable_using(substrcomparatormaker(typeofinv, 0, 3))
    @property
    def shiptype(self):
        """The shipment's type"""
        return self.typeofinv[:3]

    @comparable_using(substrcomparatormaker(typeofinv, 3, 6))
    @property
    def shiptariff(self):
        """The shipment's tariff"""
        return self.typeofinv[3:6]

    @comparable_using(substrcomparatormaker(typeofinv, 6))
    @property
    def shipmode(self):
        """The shipment's mode"""
        return self.typeofinv[6:]

invoice = session.query(Invoice).filter(Invoice.invid==2380724)
invoice = invoice.filter(Invoice.shiptype=='DPS')
invoice = invoice.filter(Invoice.shiptariff=='DOM')
invoice = invoice.filter(Invoice.shipmode=='HHG')

The point of mangling the arguments to substrcomparatormaker is so they can 
have the same start and end values as the Python slices that they're mapping 
to.  I'd rather write Python in Python than SQL in Python.

For bonus points, I guess I could've made a function that accepts (column, i, 
j) and returns the entire property definition, but this seemed useful in the 
general case.  For example, I might've wanted something like:

    @comparable_using(substrcomparatormaker(typeofinv, 0, 3))
    @property
    def shiptype(self):
        """The shipment's type"""
        return {'FOO': 'Foo invoice', 'BAR': 'Bar invoice'}[self.typeofinv[:3]]
-- 
Kirk Strauser

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@googlegroups.com
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to