Python postgreSQL sqlalchemy查询DATERANGE列

时间:2018-12-12 14:33:52

标签: python sql postgresql sqlalchemy date-range

我有一个预订系统,我将预订的日期范围保存在DATERANGE列中:

booked_date = Column(DATERANGE(), nullable=False)

我已经知道我可以使用booked_date.lowerbooked_date.upper

访问实际日期

例如,我在这里这样做:

for bdate in room.RoomObject_addresses_UserBooksRoom: 
    unaviable_ranges['ranges'].append([str(bdate.booked_date.lower),\
    str(bdate.booked_date.upper)])

现在,我需要按给定的日期范围过滤预订。例如,我想查看2018年1月1日至2018年1月1日之间的所有预订。

通常很简单,因为可以像这样比较日期:date <= other date

但是如果我用DATERANGE做到这一点:

the_daterange_lower = datetime.strptime(the_daterange[0], '%d.%m.%Y')
the_daterange_upper = datetime.strptime(the_daterange[1], '%d.%m.%Y')

bookings = UserBooks.query.filter(UserBooks.booked_date.lower >= the_daterange_lower,\
UserBooks.booked_date.upper <= the_daterange_upper).all()

我得到一个错误:

AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object associated with UserBooks.booked_date has an attribute 'lower'

编辑

我发现一个sheet带有有用的范围运算符,并且似乎有更好的选项可以完成我想做的事情,但是为此,我需要以某种方式创建一个range variable,但是python无法做到这个。所以我还是很困惑。

在数据库中,我的daterange列条目如下所示:

[2018-11-26,2018-11-28)

编辑

我正在尝试使用本机SQL,而不是sqlalchemy,但我不了解如何创建日期范围对象。

bookings = db_session.execute('SELECT * FROM usersbookrooms WHERE booked_date && [' + str(the_daterange_lower) + ',' + str(the_daterange_upper) + ')')

1 个答案:

答案 0 :(得分:1)

查询

the_daterange_lower = datetime.strptime(the_daterange[0], '%d.%m.%Y')
the_daterange_upper = datetime.strptime(the_daterange[1], '%d.%m.%Y')

bookings = UserBooks.query.\
    filter(UserBooks.booked_date.lower >= the_daterange_lower,
           UserBooks.booked_date.upper <= the_daterange_upper).\
    all()
可以使用运算符<@包含“范围包含”来实现

。为了传递正确的操作数,您必须创建一个psycopg2.extras.DateRange实例,该实例代表Python中的Postgresql daterange值:

the_daterange_lower = datetime.strptime(the_daterange[0], '%d.%m.%Y').date()
the_daterange_upper = datetime.strptime(the_daterange[1], '%d.%m.%Y').date()

the_daterange = DateRange(the_dateranger_lower, the_daterange_upper)

bookings = UserBooks.query.\
    filter(UserBooks.booked_date.contained_by(the_daterange)).\
    all()

请注意,属性lowerupperpsycopg2.extras.Range类型的一部分。 SQLAlchemy范围列类型不提供这种类型,例如您的错误状态。


如果要使用原始SQL和传递日期范围,则也可以使用相同的DateRange对象传递值:

bookings = db_session.execute(
    'SELECT * FROM usersbookrooms WHERE booked_date && %s',
    (DateRange(the_daterange_lower, the_daterange_upper),))

如果愿意,您也可以build literals manually

bookings = db_session.execute(
    'SELECT * FROM usersbookrooms WHERE booked_date && %s::daterange',
    (f'[{the_daterange_lower}, {the_daterange_upper})',))

诀窍是像往常一样使用占位符在Python中构建文字并将其作为单个值传递。它应该避免任何SQL注入的可能性;唯一可能发生的是,文字对于daterange具有无效的语法。或者,您可以将边界传递到range constructor

bookings = db_session.execute(
    'SELECT * FROM usersbookrooms WHERE booked_date && daterange(%s, %s)',
    (the_daterange_lower, the_daterange_upper))

总而言之,仅使用Psycopg2 Range类型并让它们处理细节会更容易。