2010-09-08 47 views
18

Rails 3包含保存嵌套模型時自動調用的validates_associated。該方法的問題是消息是可怕的 - 「模式(S)是無效的」針對Rails 3的更好的validates_associated方法?

已經有幾個崗位攻擊這個問題爲Rails 2:

有可能更多。如果在Rails 3兼容的這些文章中描述的版本更好,那將是一件好事。主要改進將包括爲什麼關聯模型失敗。

+1

我完全同意,這將是非常希望的。任何人都可以通過這個問題的聰明解 – sandstrom 2010-11-04 19:38:40

回答

1

validates_associated運行關聯對象類中指定的驗證。家長級別的錯誤簡單地說'我的孩子無效'。如果需要詳細信息,請暴露子對象上的錯誤(位於視圖中子表單的級別)。

5

在關係上,您可以使用:autosave => true,而不是在保存父項時嘗試保存子模型。這將自動運行孩子的驗證,他們將報告適當的錯誤消息。此外,如果您在父級必須設置的孩子上添加了狀態驗證,並且您通過關聯構造了子對象,則甚至不需要autosave標誌,並且您會收到一條美麗的錯誤消息。例如:

class Trip < ActiveRecord::Base 
    validates :name, :presence => true 

    attr_accessible :name 

    has_many :places, dependent: :destroy, :inverse_of => :trip 
end 

class Place < ActiveRecord::Base 
    belongs_to :trip 

    validates :name, :trip, presence: true 

    attr_accessible :name 
end 

然後你可以用下面的使用場景得到一個不錯的錯誤消息:

> trip = Trip.new(name: "California") 
=> #<Trip id: nil, name: "California"> 
> trip.places.build 
=> #<Place id: nil, name: nil, trip_id: nil> 
> trip.valid? 
=> false 
> trip.errors 
=> #<ActiveModel::Errors:0x00000004d36518 @base=#<Trip id: nil, name: "California">, @messages={:places=>["is invalid"]}> 
> trip.errors[:places] 
=> ["is invalid"] 

我覺得validates_associated是孩子的自動保存之前的時代的遺蹟,是不是最好的再次做事的方式。當然這不一定很好記錄。我不是100%確定這也適用於Rails 2.3,但我有一種感覺。這些更改是在添加了嵌套屬性功能(這是2.x中的某個時間)時發生的。

這是training project I posted on github的簡化代碼片段。

+0

有趣的是,提交表單經常通過關係ID,這意味着構建一個對象發生像這樣:'post.build(category_id:3,tags_ids:[1,5,7])''。這意味着對':category'或':tags'進行驗證將不起作用,因爲在模型上只設置了':category_id'和':tag_ids'。 – 2012-11-29 16:33:50

+0

這是正確的。如果這就是你所做的,你可以驗證它是否存在,例如'validates:trip_id,presence:true'。這是Rails中的一個小故障。 – 2012-11-30 19:22:22

+0

還有另一個問題,那就是':trip_id'可以被設置,但是引用了一個無效的旅程ID。 – 2012-11-30 21:38:23

3

我是有這個問題,我到底使用Ben Lee這裏給出的解決方案:

validates associated with model's error message

本說:

您可以編寫自己的自定義驗證的基礎上,內置驗證器的代碼。

查找validates_associated的源代碼,我們看到它使用「AssociatedValidator」。該源代碼是:

module ActiveRecord 
    module Validations 
    class AssociatedValidator < ActiveModel::EachValidator 
     def validate_each(record, attribute, value) 
     return if (value.is_a?(Array) ? value : [value]).collect{ |r| r.nil? || r.valid? }.all? 
     record.errors.add(attribute, :invalid, options.merge(:value => value)) 
     end 
    end 

    module ClassMethods 
     def validates_associated(*attr_names) 
     validates_with AssociatedValidator, _merge_attributes(attr_names) 
     end 
    end 
    end 
end 

所以,你可以用這個作爲一個例子來創建一個氣泡錯誤信息像這樣的自定義的驗證:

module ActiveRecord 
    module Validations 
    class AssociatedBubblingValidator < ActiveModel::EachValidator 
     def validate_each(record, attribute, value) 
     (value.is_a?(Array) ? value : [value]).each do |v| 
      unless v.valid? 
      v.errors.full_messages.each do |msg| 
       record.errors.add(attribute, msg, options.merge(:value => value)) 
      end 
      end 
     end 
     end 
    end 

    module ClassMethods 
     def validates_associated_bubbling(*attr_names) 
     validates_with AssociatedBubblingValidator, _merge_attributes(attr_names) 
     end 
    end 
    end 
end 

你可以把這個代碼在初始化,像/initializers/associated_bubbling_validator.rb

最後,你會驗證像這樣:

class User < ActiveRecord::Base 
validates_associated_bubbling :account 
end 

注:上面的代碼是完全未經測試,但如果它不完全工作,它是希望足以讓你在正確的軌道上

+2

我使用上面的代碼得到這個錯誤:undefined method'valid?' for#。任何想法是什麼問題? – Dobler 2014-06-16 08:14:50

+0

任何人都解決了@Dobler的錯誤? – albertopriore 2014-10-07 11:22:20