获得多模型关联的唯一性

时间:2016-08-06 11:44:43

标签: ruby-on-rails ruby

有三种型号:

# Table name: activities_people
#
#  activity_id :integer          not null
#  person_id   :integer          not null
#  date        :date             not null
#  id          :integer          not null, primary key
#
# Table name: activities
#
#  id          :integer          not null, primary key
#  name        :string(20)       not null
#  description :text
#  active      :boolean          not null
#  day_of_week :string(20)       not null
#  start_on    :time             not null
#  end_on      :time             not null

第三种模式是人。 关联:

class Activity < ActiveRecord::Base
  has_many :activities_people
  has_many :people, through: :activities_people
end

class ActivitiesPerson < ActiveRecord::Base
  belongs_to :person
  belongs_to :activity
end

class Person < ActiveRecord::Base
has_many :activities, through: :activities_people
end

问题: 我不知道如何在ativities_person.rb中创建验证方法来保护加入活动,并及时重叠。

示例:

person_id:1加入活动,即08.08.2016(日期)和最后09:00(start_on) - 10:00(end_on)。  同一个人想要保存到另一个活动,也将是08.08.2016和最后09:30(start_on) - 10:30(end_on)。现在 验证应该抛出他在同一时间保存到其他活动的错误(时间范围重叠)。验证首先应检查日期是否匹配,接下来应检查活动的时间重叠。

我尝试了什么:

  def check_join_client
    # check if client joined to some activities in same date as current join
    activities_date = person.activities_people.where(date: date)

    if activities_date.any?
      # get start_on and end_on for current activity and date
      get_activities = person.activities.where(id: activities_date.pluck(:activity_id))
      ol_activities = get_activities.where('((start_on <= :start_on AND
                                                    end_on >= :end_on) OR
                                                   (start_on <= :start_on AND
                                                    end_on <= :end_on AND
                                                    end_on >= :start_on) OR
                                                   (start_on >= :start_on AND
                                                    start_on <= :end_on AND
                                                    end_on >= :end_on))',
                                                  start_on: get_activities.pluck(:start_on),
                                                  end_on: get_activities.pluck(:end_on))
    end
  end

我的方法不正确。我很困惑如何解决我的问题。

1 个答案:

答案 0 :(得分:3)

根据您的数据库类型,我认为使用Postgres可以使用OVERLAP函数来执行此类查询。

结果查询可能如下:

Activity.where("(start_on, end_on) OVERLAPS (?, ?)", start_on, end_on)

另一种与DB无关的方法,但我无法测试的是:

Activity.find_by_sql("SELECT e1.* FROM activities e1, activities e2 WHERE(e2.end_on >= e1.start_on AND e2.start_on < e1.start_on)")

但是上面的方法会返回一个数组,而不是ActiveRecord :: Relation,即你不能将AR方法链接到它。它未经测试,但我认为它应该有用!

<强>更新

现在可以使用像这样的方法进行验证

class Activity
  scope :overlapping_activity, ->(activity) { where("(start_on, end_on) OVERLAPS (?, ?)", activity.start_on, activity.end_on) }
  validate :not_overlapping_activity

  def not_overlapping_activity
    if Activity.overlapping_activity(self).any?
      errors.add(:overlapping, "There is an overlapping activity for this period")
    end
  end
end

如果我能够提供帮助,请告诉我..