【问题标题】:Python postgreSQL sqlalchemy query a DATERANGE columnPython postgreSQL sqlalchemy 查询 DATERANGE 列
【发布时间】:2018-12-12 14:33:52
【问题描述】:

我有一个预订系统,我将预订的日期范围保存在 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)])

现在我需要按给定的日期范围过滤我的预订。例如,我想查看 01.01.2018 到 10.01.2018 之间的所有预订。

通常很简单,因为日期可以这样比较: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 不能这样做。所以我还是一头雾水。

在我的数据库中,我的日期范围列条目如下所示:

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

编辑

我正在尝试使用原生 SQL 而不是 sqlalchemy,但我不明白如何创建 daterange 对象。

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

【问题讨论】:

标签: python sql postgresql sqlalchemy date-range


【解决方案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()

可以使用“范围包含在”运算符&lt;@ 来实现。为了传递正确的操作数,您必须创建一个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()

请注意,lowerupper 属性是 psycopg2.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 类型并让它们处理细节会更容易。

【讨论】:

  • 感谢DateRange 的提示。我仍然有问题,但我想我现在可以继续前进。我对时区有疑问,因为我得到:sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) function daterange(timestamp without time zone, timestamp without time zone, unknown) does not exist LINE 3: WHERE usersbookrooms.booked_date &lt;@ daterange('2019-01-14T00... ^ HINT: No function matches the given name and argument types. You might need to add explicit type casts.
  • 啊,没错,不出所料,datetime.strptime 生成 datetime 对象,而不是 date 对象。添加了对date() 的调用以匹配类型。
  • 不要这样,它现在可以工作了。 un_daterange = DateRange(the_daterange_lower.date(), the_daterange_upper.date())。非常感谢,你已经帮了我很多次了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-18
  • 2013-12-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多