Rails验证一个Has Many关系至少有一个条目



我有一个事件模型。每个事件可以有多个会话。

我要确保没有至少一个会话与之关联的模型可以存在。

  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 !

我认为更简单的方法是用new而不是create创建事件和会话,它只会创建对象,但不会将它们存储到数据库中。在你创建了你的会话之后,在你的事件上做一个save

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

添加
@event.save将存储@event对象和所有相关的session对象。如果没有进一步的配置,只有新的对象是安全的,请参阅活动记录自动保存关联了解更多信息。

主要问题是您的验证与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记录,而您的原始验证将将它们全部加载到内存中,并检查关系/数组的长度。

然后,为了确保您的创建逻辑是合理的,您应该使用事务:
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!操作失败,所有操作都将安全回滚。

最新更新