简体   繁体   中英

sqlalchemy: Boolean expression for hybrid_property

I have a sqlalchemy class that represents a table with columns FileID and SettlementDate . I want to create a hybrid property to say whether a given instance is the maximum FileID for its SettlementDate , and an associated expression to use when querying. I've successfully got the property working, but am struggling with the expression. Here's the existing model:


class Hdr(model.Base):

    id = Column('ID', Integer, primary_key=True)
    file_id = Column('FileID', BIGINT, ForeignKey('FileRegister.Files.ID'))
    settlement_date = Column('SettlementDate', Date)

    @hybrid_property
    def is_latest(self):
        subquery = (
            object_session(self)
            .query(func.max(Hdr.file_id).label('file_id'))
            .group_by(Hdr.settlement_date)
            .subquery()
        )

        return (
            object_session(self)
            .query(func.count(Hdr.file_id).cast(Boolean))
            .filter(subquery.c.file_id==self.file_id)
            .scalar()
        )

I'd like to think I can do something along the lines of:

subquery = (
    select((func.max(Hdr.file_id).label('file_id'), ))
    .group_by(Hdr.settlement_date)
    .alias('a')
)

s = select(
    case(
        whens=[
            (Hdr.file_id.in_(subquery), 1)
        ],
        else_=0
    )
)

But this raises an error Boolean value of this clause is not defined .

Any help would be greatly appreciated!

Traceback follows:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
c:\Users\\venvs\insight\lib\site-packages\sqlalchemy-1.3.0b3-py3.7-win-amd64.egg\sqlalchemy\sql\selectable.py in __init__(self, columns, whereclause, from_obj, distinct, having, correlate, prefixes, suffixes, **kwargs)
   2889         try:
-> 2890             cols_present = bool(columns)
   2891         except TypeError:

c:\Users\\venvs\insight\lib\site-packages\sqlalchemy-1.3.0b3-py3.7-win-amd64.egg\sqlalchemy\sql\elements.py in __bool__(self)
    515     def __bool__(self):
--> 516         raise TypeError("Boolean value of this clause is not defined")
    517 

TypeError: Boolean value of this clause is not defined

During handling of the above exception, another exception occurred:

ArgumentError                             Traceback (most recent call last)
<ipython-input-20-4946a4bf7faa> in <module>
     10             (Hdr.file_id.in_(subquery), 1)
     11         ],
---> 12         else_=0
     13     )
     14 )

<string> in select(columns, whereclause, from_obj, distinct, having, correlate, prefixes, suffixes, **kwargs)

<string> in __init__(self, columns, whereclause, from_obj, distinct, having, correlate, prefixes, suffixes, **kwargs)

c:\Users\\venvs\insight\lib\site-packages\sqlalchemy-1.3.0b3-py3.7-win-amd64.egg\sqlalchemy\util\deprecations.py in warned(fn, *args, **kwargs)
    128                     )
    129 
--> 130             return fn(*args, **kwargs)
    131 
    132         doc = fn.__doc__ is not None and fn.__doc__ or ""

c:\Users\\venvs\insight\lib\site-packages\sqlalchemy-1.3.0b3-py3.7-win-amd64.egg\sqlalchemy\sql\selectable.py in __init__(self, columns, whereclause, from_obj, distinct, having, correlate, prefixes, suffixes, **kwargs)
   2891         except TypeError:
   2892             raise exc.ArgumentError(
-> 2893                 "columns argument to select() must "
   2894                 "be a Python list or other iterable"
   2895             )

ArgumentError: columns argument to select() must be a Python list or other iterable

The problem is

s = select(case(...))

The first argument to select() should be a sequence of column elements or from clause objects. It seems that SQLAlchemy at some point checks if the passed sequence is empty or not by doing bool(columns) . The solution is to simply wrap it in a sequence, as you have done in creating the subquery :

s = select([case(...)])

In the hybrid property's "Python side" instead of counting if the maximum file_id of any settlement_date happens to match the instance's you could filter by the instance's settlement_date and check against the maximum:

class Hdr(model.Base):

    @hybrid_property
    def is_latest(self):
        max_file_id = (
            object_session(self)
            .query(func.max(Hdr.file_id))
            .filter(Hdr.settlement_date == self.settlement_date)
            .scalar()
        )

        return max_file_id == self.file_id

In the expression you don't need to wrap the boolean expression in a scalar subquery, but return the boolean expression itself:

    @is_latest.expression
    def is_latest(cls):
        hdr_alias = aliased(Hdr)
        subquery = (
            select([func.max(hdr_alias.file_id)])
            .group_by(hdr_alias.settlement_date)
        )

        return cls.file_id.in_(subquery)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM