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
Related
I'm making up this example for the sake of demonstrating my issue. Let's say we have the following models:
class Case < ApplicationRecord
has_many :clients
accepts_nested_attributes_for :clients
end
class Client < ApplicationRecord
belongs_to :case
has_many :attorneys
accepts_nested_attributes_for :attorneys
end
class Attorney < ApplicationRecord
belongs_to :client
end
I'd like to add the same new attorney to different clients.
If I use params like this to update a case:
{
"clients_attributes"=>{
"0"=>{
"id"=>"1",
"attorneys_attributes"=>{
"123"=>{
"name"=>"Joe"
}
}
},
"1"=>{
"id"=>"2",
"attorneys_attributes"=>{
"456"=>{
"name"=>"Joe"
}
}
}
}
}
Rails will create two new attorney records with the same name "Joe". I'd like to just create one record and have both clients refer to it. Is there some clever was to reference an as yet uncreated record?
One thing I could do is check the params on the server side for identical names and do some post-update work. Is there an easier way?
Rails check if it's same record via the ID. Therefore, if you're using nested fields, but want to reuse the attorney record, then you need to make sure their ID are the same.
On the other hand, why do you want to use nested field? Usually you have a nested form to update the information, but for your case here.. that would means to update the attorney information under any of the client model - and updating attorney under one client, would actually affect all the same attorney under other clients.
As #DollarChills put in the comments, why not use a select field for the attorney under client, and create a separate page to update the attorney information?
Update: auto-complete solution
For the fields you feel can uniquely identify the an attorney, create a auto-complete field, based on that field. Let's take name for example
If the user put in the name match some DB record, load it and let the user select. You'll also have a create new attorney option
If user selected the attorney, just load it into the form with corresponding ID, so that your app knows who to update.
I guess not much help here. When you are trying to associate a user from a dropdown list, and imagine if you have 1000000 users, you would see the user instance. I need it to show the actual user's email address.
app/models/user.rb:
has_one :company
app/models/company.rb:
belongs_to :user
The link I was given has nothing to do with the dropdown's value.
Have I setup my rails association incorrectly? Funny thing was, using rails admin I had no issue in this department as I could associate a company when creating a user but not so with active admin.
All I want is when I select the User dropdown, as in picture, I'd see a list of user email addresses.
Tim was correct all this time. I needed to create a function in the User model.
app/models/user.rb:
def display_name
email
end
I've only been working with Rails for a few months now so I may be missing something obvious, but if an attribute is protected from mass assignment by attr_accessible, or attr_protected, how do you set that attribute when you first create a record? (And do it in a way that is still secure.)
For example:
Say I'm working on an application that keeps track of different procedures and processes that employees in a company have been trained on.
As part of this application, I have three models: User, Course, and TrainingClass. User represents employees, Course represents different things employees can be trained on, and TrainingClass represents a specific class in which employees will be trained on a course.
The TrainingClass has a belongs_to relationship with both the Course model (because a class is basically just a specific instance of a course) and the User model (because the class has a single instructor, who is the user who creates the TrainingClass). Similarly, the Course and User models both have a has_many relationship with the TrainingClass model.
Based on that information, how would I go about creating a new record for TrainingClass?
I could say:
course.training_classes.build(user_id: user.id)
or
user.training_classes.build(course_id: course.id)
But wouldn't that be mass assignment on the user_id or course_id attributes?
What would be the best way to do this while still keeping the application secure?
Read about Mass Assignment Security, specifically roles.
You could have
class TraningClass < ActiveRecord::Base
attr_accessible .....
attr_accessible ....., :user_id, as: :create
end
Where ...... is your list of other attributes you want accessible for mass assignment. Then when you call build, you'll pass that role
course.training_classes.build({user_id: user.id}, as: :create)
Repeat similarly for your other models. The default role will be used (the first one) unless you specify the :create role when calling build, create!, etc...
I have been trying to do this for ages and can seem to grasp it. hope someone can help ?
i have a 'message' model that has many through 'distribute' relationship to a 'contact_detail' model.
basically a message can have many contacts associated with it and a contact can have many messages.
I can get this to work and save it succesfully but i want also have a creater attribute on the 'distribute' model that i want to set to true for the creater of the message.
my form params are as follows :
{"message"=>{"message"=>"a great message ...",
"messagable_id"=>"58",
"title"=>"how are you ?",
"messagable_type"=>"MachineEnquiry",
"message_type_id"=>"1",
"contact_detail_ids"=>["2",
"2",
"11",
"7"]},
"commit"=>"Send message",
"datetime"=>""}
The 'distributes' model has a contact_detail_id' attribute and this is all saving but before save i want to set the create attribute along with a contact_detail_id.
I can so this after save but i want to validate that the creater has been set so i have to do this before save dont i ? and not sure how to do this.
Any ideas? hopefully someone can help ?
thanks in advance
rick
From the way you describe things, creator should the same for every distribution record associated with a particular message. Making much more sense to save add a new column and belongs_to relationship to Message.
class Message < ActiveRecord::Base
belongs_to :creator, :class_name => "User" # links creator to your User model
validates_presence_of :creator_id # ensures creator_id is not empty
...
end
Filling that field from the form is as simple as adding
<%= f.hidden_field :creator_id, current_user.id %>
If I'm wrong in assuming that distribution record for the same message will have the same creator, then you should look into accepts_nested_attributes_for to pass details to related models from a form.
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.