I have provider and patient models which both are belongs_to contact. On the provider and patient edit forms i use fields_for :contact to render associated contact fields.
The problem is in that i want to use different validation rules for provider.contact and patient.contact objects, i.e. i want to validate presence of contact's first_name in patient edit form, but i don't want to validate presence of first_name in provider edit form.
I tried to add dynamic validation rule in patient model:
validate :contact_first_name_blank
def contact_first_name_blank
errors.add('contact[first_name]', 'can not be blank') if contact.first_name.blank?
end
It adds error message in case of empty first_name field, but it does not hightlights contact[first_name] field.
Please help me resolve this problem, may be there is better way to do such validations.
You're adding errors to the wrong model. The square-bracket notation is only used for naming HTML form elements, not the error structure, which is specified by attribute name as far as I know.
validate :contact_first_name_blank
def contact_first_name_blank
if (contact.first_name.blank?)
errors.add_to_base('Contact first name can not be blank')
contact.errors.add('first_name', 'can not be blank')
end
end
The fields_for call checks for errors on the object passed to it, not any parent objects, as it is unaware of those relationships.
Related
I want to get the order of validation messages to go in the same order as they do on our form.
We have three classes:
class User
accepts_nested_attributes_for :pledges
end
class Pledge
accepts_nested_attributes_for :companies
validates_presence_of :pledgor_surname
end
class Company
validates_presence_of :name
end
In one form, we potentially have to take attributes for all three, so we get params like the following:
{"pledges_attributes"=>
{"0"=>
{"pledgor_surname"=>"",
"id"=>"230",
"companies_attributes"=>
{"0"=>
{"id"=>"125",
"name"=>""
}
}
}
}
}
When I call #user.update(params), it fails validation as I'd expect. But the errors#full_messages list looks like this:
["Company name can't be blank", "Pledgor surname can't be blank"]`
And the errors appear on the page in the same order.
Short of hacking the messages object, is there a way to tell Rails which order to place the messages in, or at least which of pledgor errors and company errors should go first?
No, they are returned in a hash and hashes do not provide reliable ordering. This could be overridden, which is typically done by adding files to the lib folder and specifying your overrides in the rails config.
Edit According to the comment below since Ruby 1.9.3, hashes are actually ordered, so ignore what I said.
This question is kind of hard to ask, but basically, I have a Class model and a User model, each Class table has a token, and so does each User one. After the user submits a sign up form, how would I set the value of the users class_id in the create action? I've tried <%= f.hidden_field :app_id, :value => App.find_by_token(params[:key]) %>, but this doesn't work. Sorry for the long and confusing question, will be glad to answer more. Thanks in advance for any answers
It sounds as though you have a "relationship" where a User belongs to a Class and a Class could have many users. If that is the case then you should use rails Associations to make it easy for yourself. This would involve adding a 'has_many :users' to your Class model and a 'belongs_to :class' call to your User model. You would then just use the rails helpers to 'build' the object and save it with the association in the corresponding controllers.
The manual way to do it would be as follows from your controller:
def create
#This would involve you sending the proper class id as a hidden form field with the form field attribute named 'class_id'. You may need to add 'attr_accessor :class_id' to your User model.
if user.create(user_params)
blahblahblah
else
sorry blah blah
end
end
private
def user_params
params.require(:user).permit(:name, :email, :class_id, :etc)
end
I have three models Comapny, Person and Role a Company has_many :people, a Person has_many :roles and a Company, has_many :roles, through: :people. Here's where it gets fun: The number of people to a company and roles to a person are assigned using cocoon, meaning that I have a set of dynamically generated field within a set of dynamically generated fields.
The Problem I'm having is in validating the roles for the company. For example, each company needs a President (role.role_name="President") So I set up the following: validation:
validate :final_incorporation
def company_validation
errors.add(:company, "needs a president") if self.roles.where(role_name: "President").count==0
end
...and it works! ...sometimes. If I comment out the validation and save the company with the person and the roles to the database, then put the validation back in and save it. It passes validation.
However if I add a person and then a role of "President" for that person and try to submit with validation, it doesn't pass.
My guess, though I have no idea of how to get around it, is cocoons way of assigning a temporary id to each generated field is screwing up the association specified in the validation.
So from the log:
This passes validation:
..."people_attributes"=>{"0"=>{"fname"=>"test", "lname"=>"", "roles_attributes"=>{"0"=>{"role_name"=>"President", "_destroy"=>"false", "id"=>"5"}, "1"=>{"role_name"=>"Treasurer", "_destroy"=>"false", "id"=>"6"}}}...
But this doesn't:
..."people_attributes"=>{"1455189047186"=>{"fname"=>"test", "lname"=>"", "roles_attributes"=>{"1455189059602"=>{"role_name"=>"President", "_destroy"=>"false"}, "1455189066001"=>{"role_name"=>"Treasurer", "_destroy"=>"false"}}...
Edit This also doesn't pass validation (person previously saved but roles added)
..."people_attributes"=>{"0"=>{"fname"=>"test", "lname"=>"", "roles_attributes"=>{"1455195637658"=>{"role_name"=>"President", "_destroy"=>"false"}, "1455195641046"=>{"role_name"=>"Treasurer", "_destroy"=>"false"}}...
Any ideas on how to get around this would be appreciated. Thanks in advance!
def company_validation
errors.add(:company, "needs a president") unless people.any?{|person| person.roles.any?{|role| role.role_name == "President" && !role.marked_for_destruction?}}
end
Is there an approach to conditionally prevent the creation of an object.
I have a Person class which has_many :contacts. If I try to initialize a Contact without providing an address, then the contact should not be created.
I can do this within a person object:
person.contacts << Contact.new(params[:contact]) if params[:address].present?
But can I do this within the Contact class? i.e. preventing the addition of a new Contact without the if condition above.
The reason for the question is that if I have contact fields on a form each with an address and their own contact_type in a hidden field, then the contact object would be created even if the address field is not populated.
UPDATE
Following further thoughts, the following are other options I have considered, but all have downsides:
remove from the params any contacts which do not include address:
Within the strong params method to iterate the params hash and remove any references to contacts without address params[:person][:contacts_attributes].delete_if { |key, value| value[:address].blank? }. This works, but obviously with a polymorphic model could be DRYer.
Create factory method within the Person and Business objects to define to assess the incoming request for a Person to be created and remove any contacts without address. I assume that this could be made DRY by abstracting into in a module, but this feels rather complex for this scenario.
At present option 1 above is what I am going to go with, but I'd be really interested if there is something that can be done in the Contact object.
This is based on the comment above that you do have a validation on the presence of the address field
Seems like there should be a better way to do this but, does this work for you?
new_contact = Contact.new(params)
person.contacts << new_contact if new_contact.valid?
update:
probably the right way to do this is like this
begin
person.contacts.create! params
rescue ActiveRecord::RecordInvalid => e
end
It should probably be in the controller
In the create method
def create
#contact = Contact.new(params.require(:address).permit(:phone number, contact_type)
<end>
This will not allow a contact to be created without an address but will allow a phone number and a contact type empty or not.
I believe that the answer lies in the following:
accepts_nested_attributes_for :contacts, allow_destroy:true, reject_if: proc { |attributes| attributes[:address].blank? }
If I add the reject_if proc, then any submissions without an address seem to be ignored.
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.