验证有很多关系的Rails至少有一个条目

时间:2015-08-02 22:06:49

标签: ruby-on-rails ruby validation ruby-on-rails-4 activerecord

我有一个活动模型。每个活动都可以有多个会话。

我想确保没有至少有一个与之关联的会话就不存在任何模型。

  validates :sessions, :length => { :minimum => 1 }

问题是 - 当我尝试通过调用我的模型方法来创建特定事件的会话时:

create_sessions()

其中包括:

sessions.create(event_id: id,date: x,day_of_the_week:x.strftime("%A"),classPin: pin)

表示活动的每个日期。

无法保存错误:

ActiveRecord::RecordNotSaved in EventsController#create
You cannot call create unless the parent is saved

当然 - 到目前为止还没有保存新的事件记录 - 所以由于在保存父级之前创建一个不可用的关联,因此无法创建此关联!

因此,这种关系之间的任何验证如何工作 - 因为验证发生在保存时间......但我想在保存事件之前验证会话数将大于0!

2 个答案:

答案 0 :(得分:3)

我认为更简单的方法是使用new而不是create创建事件和会话,它只会创建对象但不会将它们存储到数据库中。创建会话后,请在活动中执行save

@event = event.new(event_args)
@event.sessions.new(session_args)
@event.save

<强>加成
@event.save将存储@event对象和所有关联的session对象。如果没有进一步配置,只有对象将被保密,请参阅Active Record Autosave Association以获取更多信息。

答案 1 :(得分:2)

重点是您的验证与ActiveRecord的工作方式有冲突。你正在创造一个悖论。

有许多解决方案,但您必须放松验证。也就是说,当记录是新记录时,它不应该运行。

你可以这样定义:

class Event < ActiveRecord::Base
  validates :sessions, length: { minimum: 1 },
                       unless: :new_record?
end

或者,为了获得更大的灵活性,您可以使用自定义验证:

class Event < ActiveRecord::Base
  validate :session_count_validation

  private

  def session_count_validation
    if !new_record? && sessions.count < 1
      errors.add(:base, "Not enough sessions!")
    end
  end
end

第二种风格还具有更高性能的优势:sessions.count只会计算存在多少关联的Session记录,而原始验证将将所有这些记录加载到内存中,并检查关系/数组的长度。

然后,为了确保您的创作逻辑合理,您应该使用transaction

begin
  ActiveRecord::Base.transaction do
    event = Event.create!(args)

    sessions = dates.map do |date|
      event.sessions.build(event_id: id,
                               date: date,
                    day_of_the_week: date.strftime("%A"),
                           classPin: pin)
    end

    sessions.each(&:save!)
  end
rescue
  # your rescue logic
  # e.g. display an error to the User
end

如果任何save!操作失败,那么所有内容都将安全回滚。