has_many / belongs_to验证

时间:2014-06-21 16:13:27

标签: ruby-on-rails ruby activerecord associations has-many

考虑以下代码

class CreateFoosAndBars < ActiveRecord::Migration
  def change
    create_table :foos do |t|
      t.string :name
      t.timestamps
    end
    create_table :bars do |t|
      t.integer :foo_id
      t.string :name
      t.timestamps
    end
  end
end

class Foo < ActiveRecord::Base
  has_many :bars
end

class Bar < ActiveRecord::Base
  belongs_to :foo
  validates_presence_of :foo
end

因此,没有foo,foos可以有任意数量的条形和条形。

此代码应保存,但不会:

irb(main):020:0> foo = Foo.new
=> #<Foo id: nil, name: nil, created_at: nil, updated_at: nil>
irb(main):021:0> foo.bars.build
=> #<Bar id: nil, foo_id: nil, name: nil, created_at: nil, updated_at: nil>
irb(main):022:0> foo.save
   (0.2ms)  BEGIN
   (0.2ms)  ROLLBACK
=> false
irb(main):023:0> foo.errors
=> #<ActiveModel::Errors:0x007fb325b35e68 @base=#<Foo id: nil, name: nil, created_at: nil, updated_at: nil>, @messages={:bars=>["er ikke gyldig"]}>
irb(main):024:0> 

我正在为这个项目使用Rails 4.0.3。

我似乎无法掌握如何以事务方式初始化,验证和保存具有多个条形的foo。我在这里缺少什么?

4 个答案:

答案 0 :(得分:3)

您的代码应该可以使用,我将您的示例粘贴到新的rails应用程序中:

class CreateFoosAndBars < ActiveRecord::Migration
  def change
    create_table :foos do |t|
      t.string :name
      t.timestamps
    end
    create_table :bars do |t|
      t.integer :foo_id
      t.string :name
      t.timestamps
    end
  end
end

class Foo < ActiveRecord::Base
  has_many :bars
end

class Bar < ActiveRecord::Base
  belongs_to :foo
  validates_presence_of :foo
end

这些是结果:

2.1.1 :001 > Foo.count
   (0.1ms)  SELECT COUNT(*) FROM "foos"
 => 0
2.1.1 :002 > Bar.count
   (0.1ms)  SELECT COUNT(*) FROM "bars"
 => 0
2.1.1 :003 > foo = Foo.new
 => #<Foo id: nil, name: nil, created_at: nil, updated_at: nil>
2.1.1 :004 > foo.bars.build
 => #<Bar id: nil, foo_id: nil, name: nil, created_at: nil, updated_at: nil>
2.1.1 :005 > foo.save
   (0.3ms)  SAVEPOINT active_record_1
  SQL (0.5ms)  INSERT INTO "foos" ("created_at", "updated_at") VALUES (?, ?)  [["created_at", "2014-06-21 16:24:15.334535"], ["updated_at", "2014-06-21 16:24:15.334535"]]
  SQL (0.2ms)  INSERT INTO "bars" ("created_at", "foo_id", "updated_at") VALUES (?, ?, ?)  [["created_at", "2014-06-21 16:24:15.342524"], ["foo_id", 1], ["updated_at", "2014-06-21 16:24:15.342524"]]
   (0.1ms)  RELEASE SAVEPOINT active_record_1
 => true
2.1.1 :006 > foo.errors
 => #<ActiveModel::Errors:0x007ff37b9514a8 @base=#<Foo id: 1, name: nil, created_at: "2014-06-21 16:24:15", updated_at: "2014-06-21 16:24:15">, @messages={}>
2.1.1 :007 > Foo.count
   (0.2ms)  SELECT COUNT(*) FROM "foos"
 => 1
2.1.1 :008 > Bar.count
   (0.3ms)  SELECT COUNT(*) FROM "bars"
 => 1

这是使用Rails 4.1.0。

在4.1.0之前的Rails版本中,您必须指定以下关系:

class Foo
  has_many :bars, inverse_of: :foo
end

然而,Rails 4.1.0现在启发式地检测到反向关联,如记录here

答案 1 :(得分:0)

foo = Foo.build
bar = foo.bars.build
bar.save

原因是你在不保存foo的情况下创建条形图。因此foo无效。你必须先保存foo,这样才能得到一个id。

更新:

你必须在你的酒吧模型中使用inverse_of。

belongs_to :foo, inverse_of :bar

Foo模型

has_many :bards, inverse_of :foo

参考:http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html

答案 2 :(得分:0)

尝试将inverse_of选项添加到两个关联中。也许Rails在某个地方变得混乱,无法与之匹敌。

答案 3 :(得分:0)

我发现了解决方案。

如果我这样做

foo = Foo.new
bar = foo.bars.build
bar.foo
=> nil

所以build撒谎。它并没有像你期望的那样建立一个与foo相关联的栏。 build仅在foo持久化时有效。

但是,如果我将inverse_of: :foo添加到我的Foo模型关联中,如下所示:

class Foo < ActiveRecord::Base
  has_many :bars, inverse_of: :foo
end

它突然奇怪的工作。在构建的条形实例上调用foo会突然返回一个对象而不是nil。

irb(main):002:0> bar = foo.bars.build
=> #<Bar id: nil, foo_id: nil, name: nil, created_at: nil, updated_at: nil>
irb(main):003:0> bar.foo
=> #<Foo id: nil, name: nil, created_at: nil, updated_at: nil>
irb(main):005:0> bar.foo.object_id
=> 70208007135620
irb(main):006:0> foo.object_id
=> 70208007135620

我不知道为什么这是必要的而不是默认行为。

更新

此问题仅适用于4.1.0之前的Rails版本。