使用collection_singular_ids = ids方法时,Rails 3无法对持久对象执行验证

Man*_*han 5 has-many-through model-associations ruby-on-rails-3

有没有办法避免在分配集合属性时自动保存对象(collection_singular_ids = ids方法)?

例如,我有以下测试和包模型,包有很多测试.用户可以使用多个测试构建包捆绑包.

# test.rb
class Test < ActiveRecord::Base
end

# package.rb
class Package < ActiveRecord::Base
  has_many :package_tests 
  has_many :tests, :through => :package_tests
  belongs_to :category

  validate :at_most_3_tests

  private
  # tests count will differ depends on category.
  def at_most_3_tests
    errors.add(:base, 'This package should have at most three tests') if  test_ids.count > 3
  end
end

# package_test.rb
class PackageTest < ActiveRecord::Base
  belongs_to :package
  belongs_to :test

  validates_associated :package
end
Run Code Online (Sandbox Code Playgroud)

当包对象是新的时,验证没有问题.

1.9.2 :001> package = Package.new(:name => "sample", :cost => 3.3, :test_ids => [1,2,3,4])
=> #<Package id: nil, name: "sample", cost: 3.3> 
1.9.2 :002> package.test_ids
=> [1, 2, 3, 4] 
1.9.2 :003> package.save
=> false 
1.9.2 :004> package.save!
ActiveRecord::RecordInvalid: Validation failed: This package should have at most three tests
1.9.2: 005> package.test_ids = [1,2]
=> [1, 2] 
1.9.2 :005> package.save!
=> true
Run Code Online (Sandbox Code Playgroud)

但是我无法用持久化的包对象来命中at_most_3_tests方法.

在分配测试ID时立即创建连接表记录

1.9.2: 006> package
=> #<Package id: 1, name: "sample", cost: 3.3> 
1.9.2: 007> package.test_ids
=> [1,2]
1.9.2: 007> package.test_ids = [1,2,3,4,5]
=> [1,2,3,4,5]
1.9.2: 008> package.test_ids 
=> [1,2,3,4,5]
Run Code Online (Sandbox Code Playgroud)

客户端要求是下拉界面,用于选择包形式的多个测试,还使用select2 jquery插件进行下拉.Rhmtl代码看起来像

<%= form_for @package do |f| %>
  <%= f.text_field :name %>
  <div> <label>Select Tests</label> </div>
  <div>
    <%= f.select "test_ids", options_for_select(@tests, f.object.test_ids), {}, { "data-validate" => true, :multiple => true} %>
  </div>
Run Code Online (Sandbox Code Playgroud)

请帮我解决这个问题.

Moh*_*out 5

限制关联数量

您可以使用以下验证,而不是您的方法:

has_many :tests, :length => { :maximum => 3 }
Run Code Online (Sandbox Code Playgroud)

对于使用多重选择

我之前有这个问题,我使用以下代码解决了它:

<%= f.select(:test_ids, options_from_collection_for_select(@tests, :id, :name,  @promotion.test_ids), {}, {multiple: true, "data-validate" => true}) =>
Run Code Online (Sandbox Code Playgroud)

我认为options_from_collection_for_select,从这个链接中读取帖子示例的类别可能对您有所帮助.

验证

我使用了validates_associated,如下所示:

 validates_associated :tests
Run Code Online (Sandbox Code Playgroud)

用于获取持久对象的旧属性

您可以使用reload进行活动记录,如下所示:

1.9.2: 006> package
=> #<Package id: 1, name: "sample", cost: 3.3> 
1.9.2: 007> package.test_ids
=> [1,2]
1.9.2: 007> package.test_ids = [1,2,3,4,5]
=> [1,2,3,4,5]
1.9.2: 007> package.reload
=> #<Package id: 1, name: "sample", cost: 3.3> 
1.9.2: 008> package.test_ids 
=> [1,2]
Run Code Online (Sandbox Code Playgroud)

或者您可以检查包对象的验证,如果是false则重新加载它:

unless package.valid?
  package.reload
end
Run Code Online (Sandbox Code Playgroud)