保存前添加到集合

时间:2013-10-29 18:09:14

标签: ruby-on-rails ruby

我正在试图弄清楚如何做这样的事情:

event = Event.new
loc = Location.new
loc.name = "test"
loc.save
event.locations << loc
event.save!

事件和位置有多对多的关系。但是,我一直收到这个错误:

ActiveRecord::RecordInvalid: Validation failed: Event locations is invalid

如果我先保存事件,这样可以正常工作,但是我在我工作的上下文中没有这个选项。

以下是我的模特:

class Event < ActiveRecord::Base
  #belongs_to :user
  has_many :event_locations
  has_many :locations, :through => :event_locations

  accepts_nested_attributes_for :locations
end

class EventLocation < ActiveRecord::Base
  belongs_to :event
  belongs_to :location

  validates_presence_of :event
  validates_presence_of :location

  accepts_nested_attributes_for :location
end

class Location < ActiveRecord::Base
  has_many :event_locations
  has_many :events, :through => :event_locations
end

现在我发现连接模型EventLocation上的验证会导致此问题。

我应该不验证这个吗?有没有不同的方式呢?

1 个答案:

答案 0 :(得分:1)

<强> TL; DR

尝试a)

class EventLocation < ActiveRecord::Base
  belongs_to :event
  belongs_to :location

  validates_presence_of :event_id
  validates_presence_of :location_id

  accepts_nested_attributes_for :location
end

或 - 如果您不需要EventLocation - b)

class Event < ActiveRecord::Base
  #belongs_to :user
  has_and_belongs_to_many :locations

  accepts_nested_attributes_for :locations
end

class Location < ActiveRecord::Base
  has_and_belongs_to_many :events
end

发生了什么事?

在“碰撞”模型EventLocation中,您验证了:location:event的存在,这会导致您的问题。

要理解为什么会发生这种情况以及如何解决这个问题,我们首先需要了解验证系统的工作原理以及将模型添加到:through - 集合的内容。

<强>验证

在这个特殊情况下,我们validate_presence_of会告诉模型看一下这个东西,看看它是否存在。

简化为:

validates_presence_of :something
保存时

导致

model.save if model.something.present?

所以这个应该检查正确的事情。

:through - 集合

怎么样?

由于以上显然不能按预期的方式工作,我们可以推断出

event.locations << loc

实际上没有设置

EventLocation.new(location: loc)

所以真正可能发生的是,rails'只是'设置ID

EventLocation.new(location_id: loc.id)

这是什么意思?

假设上述内容是正确的,那么验证id的存在而不是关联的对象可能会解决这个问题。

为什么要这么麻烦?

当然,验证通常不是一个坏主意,但这里可能有另一种选择,那就是has_and_belongs_to_many。这些方法包含了大量的魔力,使模型能够处理不需要附加模型的碰撞表。

一般来说,如果你不真的需要将任何业务逻辑或附加数据附加到两个表的碰撞中,依靠魔法内置(habtm)做正确的事情为你而不是手动做(并验证)它。