I've come across reject_if in cocoon's README, and also in the documentation for Nested Attributes. What is the rationale for using reject_if when the associated active record objects can determine whether they're valid or not?
For nested objects, you may not want to attempt to submit a record that is :all_blank or for any other reason you may want to check on. The point being, an empty or incomplete (in some way) object can, this way, simply not be built / added to the nested objects collection.
Validations serve a different purpose. If an empty object, say, fails validation then the whole form submit will fail. The reject_if approach allows submission to succeed in such a case by removing the object from consideration before validations fire.
The Rails guides has a description of the rationale for :reject_if, though it doesn't explicitly compare this option to just validating the sub-objects:
9.5 Preventing Empty Records
It is often useful to ignore sets of fields that the user has not
filled in. You can control this by passing a :reject_if proc to
accepts_nested_attributes_for. This proc will be called with each
hash of attributes submitted by the form. If the proc returns false
then Active Record will not build an associated object for that hash.
The example below only tries to build an address if the kind
attribute is set.
class Person < ActiveRecord::Base
has_many :addresses
accepts_nested_attributes_for :addresses, reject_if: lambda {|attributes| attributes['kind'].blank?}
end
As a convenience you can instead pass the symbol :all_blank which
will create a proc that will reject records where all the attributes
are blank excluding any value for _destroy.
Related
I have two models
class Information < ActiveRecord::Base
belongs_to :study
validates_presence_of :email
end
and
class Study < ActiveRecord::Base
has_many :informations
accepts_nested_attributes_for :informations
end
I show up a form of study which contains few fields for the informations and i want to validate presence of those fields. Only on validation success i wanted to save the study field values as well and i wanted to show errors if the validation fails. How can i do this? Thanks in advance.
You write validations in the models that you require, as normal. So if you need to validate presence of field foo in the Information class you'd just write validates_presence_of :foo in that class. Likewise validations for Study fields just go in the Study class. With nested attributes, when you update a Study instance from a params hash that contains nested attributes, it'll update the Information instance(s) too, running validations in passing. That's what the accepts_nested_attributes_for call is doing - it's giving "permission" for the appropriate bits of a params hash to be used in this way.
You can use reject_if to only reject new nested records should they fail to meet criteria. So I might let someone create a Study and only create one or more nested Information instances associated with that Study if they'd filled in field(s) in the form, but if they left them blank, the nested stuff wouldn't be created and saved (so you don't get pointless blank associated records). The Study would still be saved. For example:
accepts_nested_attributes_for(
:informations,
reject_if: proc() { | attrs | attrs[ 'title' ] .blank? }
)
This and more is covered in the API documentation here:
http://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html
Beware that nested fields are intended for existing records only. If you were creating a new Study instance in a new/create action with no Information instances associated, you won't see any nested form fields for your Information class at all - when you might be expecting just one, for a blank new item. This can be very confusing if you aren't ready for it! You'll need to manually add a new Information instance to your Study instance in the controller or similar for the 'new' and 'create' actions, e.g. using before_filter :create_blank_object, only: [ :new, :create ], with, say:
def create_blank_object
#study = Study.new
#study.informations << Information.new
end
you can use validates_presence validation available in rails other wise you can write before_create or before_save callback method. write validation logic inside the before_create or before_save callback method.
Check out the API Doc for validates_associated:
Validates whether the associated object or objects are all valid. Works with any kind of association.
If you call a method on the parent object which runs validations (e.g. save), the validation on the associated objects will be called as well.
I am having a very strange problem in Rails. I am using update_attributes on a Parent Object to update all the children objects. The children objects have (custom) validation and indeed this works ok, meaning that if I give wrong values the validation trigger and I get an error back.
Now I am in a strange situation where one of the model is invalid in the database (let's not question why, let's just say I can go in the DB and run some SQL to make the model invalid). If I go in my app I can see the invalid values and this is fine. I fix the values and save again and I can see, stepping in the ruby code that the validation is called also BEFORE saving the new values, meaning that I will get an error and Rails will never execute the SQL to actually update the values to the correct ones.
I hope the above makes sense. Do you have any idea or do you think there is something I am overlooking?
SOLUTION:
What was happening was that a many-to-many relationship was validating the existing DB data before being replaced by the new data. Basically the structure was like this:
class User
has_many :user_permissions
has_many :permissions, :through => :model_permissions
class Permission
has_many :user_permissions
has_many :users, :through => :user_permissions
class UserPermission
belongs_to :user
belongs_to :permission
validates_associated :user # THIS was causing the problem
validates_associated :permission # and THIS as well
I simply removed the validates_associated directive, since I am validating the linked records independently anyway.
Well, Rails does run your validation prior to writing the data to the database (please refer to Active Record callback sequence), so having validation errors means that some piece of the model you are trying to save is not valid. It might an associated model containing errors with the validation turned on or just some missing part – in any case, just have a look at what are the errors you are getting.
In case (let's no question why either :) you want to skip validation - you are open to choose from #update_attribute (to update just one attribute), calling #save(false), using +udpate_all method of the model class or even go down to ActiveRecord::Base.connection.execute – none of these will ever bother you with validation errors :)
In Rails 2.3.5 model I am using
accepts_nested_attributes_for :questions, :reject_if => lambda { |a| a[:content].blank? }
But its rejecting if there is the textbox is not empty..
How can I make it to reject only if there is nothing entered into the textbox ?
Are you confusing reject_if with record validation? The reject_if merely tells the app to ignore that set of nested attributes if a condition is true. In your case, the question's attributes will be ignored if the question's content is blank. If you want to validate or otherwise ensure that the question record(s) have a non blank value for content, you'd put validation in your question model.
You also might consider changing lambda{} to proc{}.
reject if will save the parent object and any other amount of child objects rejecting only those that fail the reject_if condition. If this is what you want then it is fine, i suggest debugging a little bit, put in a print statement or something, maybe
lambda { |a| puts a.inspect; a[:content].blank? }
If you want the whole nested object to save all at once, then use validations.
Let's say I have an ActiveRecord model called Book that has a has_many association with a model Pages.
class Book < ActiveRecord::Base
has_many :pages
end
I'd like to know if there is an established method of ensuring that a Book object cannot be saved to the database without having at least one valid Page object associated with it. My goal is not to test the presence of an association, but to validate that a parent object indeed has a valid child object. Does this make sense? Is this actually a case of testing an association? I'm familiar with the "validates_associated" method, but this validation will not fail if the association hasn't been assigned, but how do I ensure that there is a valid object on the other side of the association?
From the Rails 2.3.2 documentation for validates_associated:
NOTE: This validation will not fail if
the association hasn’t been assigned.
If you want to ensure that the
association is both present and
guaranteed to be valid, you also need
to use validates_presence_of.
I currently have a nested forms model. The nested forms for the paperclip attachments work fi and don't populate the DB with blanks, but another one I have for quotes always saves one blank quote along with my main model when I create a new one. How can I just have it silently fail and bypass writing this to the db? It's an optional field so I don't want to give them an error.
Use the following option on accepts_nested_attributes
accepts_nested_attributes_for :quotes, :reject_if => :all_blank
If you want to be more specific about when the record is considered blank, :reject_if also can take a proc.