我有一个活动模型。每个活动都可以有多个会话。
我想确保没有至少有一个与之关联的会话就不存在任何模型。
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!
答案 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!
操作失败,那么所有内容都将安全回滚。