Rails:使用Ransack

时间:2015-08-18 16:53:29

标签: ruby-on-rails postgresql ruby-on-rails-4 ransack

我有一个包含2列的Schedule db表:

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视图中,我想展示一个可以执行以下三项操作的过滤器:

  • 使用starts_at&的时间过滤所有时间表忽略日期时ends_at
  • 使用starts_at&中的日期过滤所有日程安排ends_at而忽视时间
  • 使用starts_at&的日期和时间过滤所有日程安排。 ends_at

我的代码:

# 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选择菜单中选择开始时间,那么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

如果我从日期选择中选择日期而忽略两个时间选择菜单,那么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

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

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

如果我同时添加include_blank: true选项,然后选择日期,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

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

1 个答案:

答案 0 :(得分:0)

知道了。

# 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