Rails:使用 Ransack 过滤日期和/或时间

Rails: Filter date or time or both using Ransack

我有一个 Schedule 数据库 table 有 2 列:

t.datetime "starts_at"
t.datetime "ends_at"

并且当用户创建计划时,相同的日期将保存在这两列中:

starts_at: 2015-08-22 16:00:00 UTC
ends_at: 2015-08-22 16:30:00 UTC

Schedule#index 视图中,我想展示一个可以做这三件事的过滤器:

我的代码:

# Schedule controller
def index
  @q          = Schedule.ransack(params[:q])
  @q.sorts    = ['starts_at asc', 'ends_at asc'] if @q.sorts.empty?
  @schedules  = @q.result.where('starts_at >= ?', Time.zone.today)
end

# view
<%= search_form_for @q do |f| %>
  <%= f.time_select :starts_at_gteq, ampm: true, default: { hour: '07' } %>
  <%= f.time_select :ends_at_lteq, ampm: true, default: { hour: '23' } %>

  <%= f.select :starts_at_eq, options_for_select([
      ['Today', Time.zone.today],
      ['Tomorrow', Time.zone.tomorrow],
      [(Time.zone.today + 2).strftime("%d %b"), Time.zone.today + 2],
      [(Time.zone.today + 3).strftime("%d %b"), Time.zone.today + 3],
      [(Time.zone.today + 4).strftime("%d %b"), Time.zone.today + 4],
      [(Time.zone.today + 5).strftime("%d %b"), Time.zone.today + 5],
      [(Time.zone.today + 6).strftime("%d %b"), Time.zone.today + 6]
    ]), include_blank: true %>
...

此代码的问题在于,如果我从 starts_at select 菜单中选择开始时间而忽略日期菜单,那么 Ransack 将尝试使用今天的日期进行过滤:

SELECT "schedules".* FROM "schedules" WHERE ("schedules"."starts_at" >= '2015-08-18 16:00:00.000000' AND "schedules"."ends_at" <= '2015-08-18 23:00:00.000000') ORDER BY "schedules"."starts_at" ASC, "schedules"."ends_at" ASC

如果我从日期 select 中选择一个日期,同时忽略两个时间 select 菜单,那么 Ransack 将这样做:

SELECT "schedules".* FROM "schedules" WHERE ("schedules"."starts_at" = '2015-08-22 00:00:00.000000' AND "schedules"."starts_at" >= '2015-08-18 07:00:00.000000' AND "schedules"."ends_at" <= '2015-08-18 23:00:00.000000') ORDER BY "schedules"."starts_at" ASC, "schedules"."ends_at" ASC

如果我在时间 select 菜单中添加 ignore_date: true 选项,并且如果我从 select 菜单中选择开始时间,那么 Ransack 将完全忽略时间 select :

SELECT "schedules".* FROM "schedules" ORDER BY "schedules"."starts_at" ASC, "schedules"."ends_at" ASC

如果我将 include_blank: true 选项添加到两个时间 select 然后选择一个日期,Ransack 将使用日期 时间在凌晨 12 点进行过滤:

SELECT "schedules".* FROM "schedules" WHERE "schedules"."starts_at" = '2015-08-22 00:00:00.000000' ORDER BY "schedules"."starts_at" ASC, "schedules"."ends_at" ASC

我完全迷失在这里。感谢您的帮助。

知道了。

# Schedule model
ransacker :start_date, type: :date do
  Arel.sql('starts_at::date')   # filter only the date from starts_at attribute
end

ransacker :start_time, type: :time do
  Arel.sql('starts_at::time')   # filter only the time from starts_at attribute
end

ransacker :end_time, type: :time do
  Arel.sql('ends_at::time')     # filter only the time from ends_at attribute
end

# view
<%= search_form_for @q do |f| %>
  <%= f.time_select :start_time_gteq, ampm: true, default: { hour: '07' } %>
  <%= f.time_select :end_time_lteq, ampm: true, default: { hour: '23' } %>

<%= f.select :start_date_eq, options_for_select([
        ['Today', Time.zone.today],
        ['Tomorrow', Time.zone.tomorrow],
        [(Time.zone.today + 2).strftime("%d %b"), Time.zone.today + 2],
        [(Time.zone.today + 3).strftime("%d %b"), Time.zone.today + 3],
        [(Time.zone.today + 4).strftime("%d %b"), Time.zone.today + 4],
        [(Time.zone.today + 5).strftime("%d %b"), Time.zone.today + 5],
        [(Time.zone.today + 6).strftime("%d %b"), Time.zone.today + 6]
      ], @q.start_date_eq), include_blank: true %>
...

SQL 查询的内容:

SELECT "schedules".* FROM "schedules" WHERE (starts_at::date = '2015-08-22' AND starts_at::time >= '2015-08-19 12:15:00.000000' AND ends_at::time <= '2015-08-19 16:45:00.000000') AND (starts_at >= '2015-08-19') ORDER BY "schedules"."starts_at" ASC, "schedules"."ends_at" ASC