SA:我可以为Date列添加一个“ year” column_property吗?

Luk*_*404 2 python sqlalchemy properties declarative

我在sqlalchemy-0.7中有一个带有Date列的类。我可以使用column_property或类似的东西来获取年份并让我轻松地对其进行过滤吗?我怎么写呢?

IE,我想拥有(声明性语法):

class Foo(Base):
    id   = Column(Integer, primary_key=True)
    date = Column(Date(), nullable=False)
    year = column_property(something here)

# later on
q = session().query(Foo).filter_by(year=2011)
Run Code Online (Sandbox Code Playgroud)

Den*_*ach 5

当然可以定义这样的属性:

year = column_property(extract('year', date))
Run Code Online (Sandbox Code Playgroud)

但是您真的需要吗?您可以按年份过滤,而无需通过重写过滤条件来定义此类属性:

query(Foo).filter(extract('year', Foo.date)==2011)
Run Code Online (Sandbox Code Playgroud)

更新资料

尽管此解决方案看起来很简单,但它也有一个缺点:WHERE子句中的这种条件永远不会在日期字段上使用索引。具有很多行和较高的条件选择性,这将对性能产生很大影响。因此,您可能希望重写将导致RANGE INDEX SCAN而不是FULL TABLE SCAN的条件(如Simon的评论中所建议):

start = datetime.date(year, 1, 1)
end = datetime.date(year, 12, 31)
query(Foo).filter(Foo.date.between(start, end))
Run Code Online (Sandbox Code Playgroud)

也可以使用这种行为来定义属性,您只需要重新定义比较器即可:

class YearComparator(ColumnProperty.Comparator):
    def __eq__(self, year):
        if isinstance(year, int):
            column = self.prop.columns[0].get_children()[0].expr
            start = datetime.date(year, 1, 1)
            end = datetime.date(year, 12, 31)
            return column.between(start, end)
        else:
            # It can be a column or exression which we can't handle such way
            return ColumnProperty.Comparator.__eq__(self, year)
    # __lt__, __gt__ etc. are very similar to __eq__

def year_property(date_column, **kwargs):
    kwargs.setdefault('comparator_factory', YearComparator)
    return column_property(extract('year', date_column), **kwargs)

class Foo(Base):
    __tablename__ = 'Foo'
    id = Column(Integer, primary_key=True)
    date = Column(Date, index=True)
    year = year_property(date)
Run Code Online (Sandbox Code Playgroud)