How to access associated table attributes in validation before they are saved in Ruby on Rails
I want to be able to skip validation if a certain attribute is set to false, say status, problem is this model has many nested attributes to them, and they need to skip validation too if status is false.
The purpose of such implantation is that if one wanted to save a draft of there form entry, for whatever reason, having it going through validation could halt them saving it, which is not desirable behavior, and only need to validate entries that are going public by setting status to true.
Example below
attribute status located in Article
model Article
has_many :sub_articles
accepts_nested_attributes_for :sub_articles, :allow_destroy => true
validate_presence_of :body, :unless => Proc.new{|article| !article.status }
model SubArticle
belongs_to :article
has_many :foos
accepts_nested_attributes_for :foos, :allow_destroy => true
validate_presence_of :body, :unless => Proc.new{|sub_article| !sub_article.article.status }
model Foo
belongs_to :sub_article
validate_presence_of :body, :unless => Proc.new{|foo| !foo.sub_article.article.status }
validation skipping for Article will work, but will not for SubArticle or Foo since they have not been saved an there ids are not set, not making them able to traverse up the associations like in the example.
Is it possible to access associated table attributes at the point of validation?
Any help would be greatly appreciated.
------Updated------
The reason validation for SubArticle and Foo fail is because of a
undefined method `status' for nil:NilClass
This is expected because of the time of validation the article_id or sub_article_id is still nil, I've comfirmed this by doing
model SubArticle
belongs_to :article
has_many :foos
validate_presence_of :body, :unless => Proc.new{|sub_article| !sub_article.article.check_attributes }
def check_attributes
pp self
end
witch gives
#<SubArticle id: nil, body: "", article_id: nil, created_at: nil, updated_at: nil &开发者_C百科gt;
-----updated 2----------
I forgot and important detail, all of the data for the models are entered at once using a nested form, so at the moment of create, all of the entries on all of the models are only in memory, fixing example code so that would be more obvious.
It is possible but you need to use =>
instead of =
. Like this:
validate_presence_of :body, :unless => Proc.new{|article| !article.status }
Please look at: http://guides.rubyonrails.org/active_record_validations_callbacks.html#conditional-validation for more information about conditional validations.
What about conditionally validating on the ID?
model Article
has_many :sub_articles
validate_presence_of :body, :unless => Proc.new{|article| !article.status }
model SubArticle
belongs_to :article
has_many :foos
validate_presence_of :body, :unless => Proc.new{|sub_article| !sub_article.article_id}
model Foo
belongs_to :sub_article
validate_presence_of :body, :unless => Proc.new{|foo| !foo.sub_article_id }
Not sure if this will work but you're essentially only running validation if the parent model has saved and therefore has an ID. Won't work for updates though. Perhaps a mix between this and your original code would do for update?
I've got it working, although hack-ish, solved the can't traverse associations since ids are nil by using the :inverse_of
model Article
has_many :sub_articles, :inverse_of => :article
validate_presence_of :body, :unless => Proc.new{|article| !article.status }
model SubArticle
belongs_to :article, :inverse_of => :sub_articles
has_many :foos, :inverse_of => :foo
validate_presence_of :body, :unless => Proc.new{|sub_article| !sub_article.article.status }
model Foo
belongs_to :sub_article, :inverse_of => :foos
validate_presence_of :body, :unless => Proc.new{|foo| !foo.sub_article.article.status }
Which will make the instance being examined in all layers have access to the status attribute.
精彩评论