Nested forms in rails - accessing attribute in has_many relation - ruby-on-rails

I have a user and a profile model. One user can have many profiles. I need to access only one information from the profiles section (viz the phone number) in my user model during the user creation process. Hence I'm trying to get it done through attr_accessible. My user.rb looks like this.
has_many :profiles
attr_accessible :handle, :email, :password, :profile_mobile_number
attr_accessor : :profile_mobile_number
The problem that I'm facing is that when I try to call the getter method profile_mobile_number in a method in user.rb (the method is private, though I think it doesn't matter), I'm getting a null value. I use the following in my users/new.html.erb form
My question is what is the right way to do this? Should I use <% f.fields_for :profile do |ff| -%> or <% f.fields_for :profiles do |ff| -%> (notice that the second one is plural). When I use the plural :profiles, I don't even see the fields on the form. What am I missing here? And what is the tense that needs to be used in model user.rb? :profile_phone_number or :profiles_phone_number? Thanks.

You could do something like the following:
<% form_for #user, :url => { :action => "update" } do |user_form| %>
...
<% user_form.fields_for :profiles do |profiles_fields| %>
Phone Number: <%= profiles_fields.text_field :profile_mobile_number %>
<% end %>
<% end %>
But since you already have an association, then might as well use 'accepts_nested_attributes_for'

You should watch RailsCasts Nested Model Form.
thanks Ryan Bates great work.

http://apidock.com/rails/v3.2.8/ActionView/Helpers/FormHelper/fields_for
This api dock link list many Nested Attributes Examples including one-to-one, one-to-many. It's very helpful!

You can use 'accepts_nested_attributes_for' to do this; but there's a little trick in forms:
You must use the singular, and call fields_for for each profile, like this:
<% form_for #user do |f| -%>
<% #user.profiles.each do %>
<% f.fields_for :profile_attributes, profile do |ff| -%>
<% end %>
Notice that is :profile_attributes, instead of just :profile.

Related

Rails: Create Model and join table at the same time, has_many through

I have three Models:
class Question < ActiveRecord::Base
has_many :factor_questions
has_many :bigfivefactors, through: :factor_questions
accepts_nested_attributes_for :factor_questions
accepts_nested_attributes_for :bigfivefactors
end
class Bigfivefactor < ActiveRecord::Base
has_many :factor_questions
has_many :questions, through: :factor_questions
end
and my join-table, which holds not only the bigfivefactor_id and question_id but another integer-colum value.
class FactorQuestion < ActiveRecord::Base
belongs_to :bigfivefactor
belongs_to :question
end
Creating an new Question works fine, using in my _form.html.erb
<%= form_for(#question) do |f| %>
<div class="field">
<%= f.label :questiontext %><br>
<%= f.text_field :questiontext %>
</div>
<%= f.collection_check_boxes :bigfivefactor_ids, Bigfivefactor.all, :id, :name do |cb| %>
<p><%= cb.check_box + cb.text %></p>
<% end %>
This let's me check or uncheck as many bigfivefactors as i want.
But, as i mentioned before, the join model also holds a value.
Question:
How can I add a text-field next to each check-box to add/edit the 'value' on the fly?
For better understanding, i added an image
In the console, i was able to basically do this:
q= Question.create(questiontext: "A new Question")
b5 = Bigfivefactor.create(name: "Neuroticism")
q.bigfivefactors << FactorQuestion.create(question: q, bigfivefactor: b5, value: 10)
I also found out to edit my questions_controller:
def new
#question = Question.new
#question.factor_questions.build
end
But i have no idea how to put that into my view.
Thank you so much for your help!
Big Five Factors model considerations
It looks like your Bigfivefactors are not supposed to be modified with each update to question. I'm actually assuming these will be CMS controlled fields (such that an admin defines them). If that is the case, remove the accepts_nested_attributes for the bigfivefactors in the questions model. This is going to allow param injection that will change the behavior sitewide. You want to be able to link to the existing bigfivefactors, so #question.factor_questions.first.bigfivefactor.name is the label and #question.factor_questions.first.value is the value. Notice, these exist on different 'planes' of the object model, so there wont be much magic we can do here.
Parameters
In order to pass the nested attributes that you are looking for the paramater needs to look like this:
params = {
question: {
questiontext: "What is the average air speed velocity of a sparrow?",
factor_questions_attributes: [
{ bigfivefactor_id: 1, value: 10 },
{ bigfivefactor_id: 2, value: 5 } ]
}
}
Once we have paramaters that look like that, running Question.create(params[:question]) will create the Question and the associated #question.factor_questions. In order to create paramaters like that, we need html form checkbox element with a name "question[factor_questions_attributes][0][bigfivefactor_id]" and a value of "1", then a text box with a name of "question[factor_question_attributes][0][value]"
Api: nested_attributes_for has_many
View
Here's a stab at the view you need using fields_for to build the nested attributes through the fields for helper.
<%= f.fields_for :factor_questions do |factors| %>
<%= factors.collection_check_boxes( :bigfivefactor_id, Bigfivefactor.all, :id, :name) do |cb| %>
<p><%= cb.check_box + cb.text %><%= factors.text_field :value %></p>
<% end %>
<% end %>
API: fields_for
I'm not sure exactly how it all comes together in the view. You may not be able to use the built in helpers. You may need to create your own collection helper. #question.factor_questions. Like:
<%= f.fields_for :factor_questions do |factors| %>
<%= factors.check_box :_destroy, {checked => factors.object.persisted?}, '0','1' %> # display all existing checked boxes in form
<%= factors.label :_destroy, factors.object.bigfivefactor.name %>
<%= factors.text_box :value %>
<%= (Bigfivefactor.all - #question.bigfivefactors).each do |bff| %>
<%= factors.check_box bff.id + bff.name %><%= factors.text_field :value %></p> # add check boxes that aren't currently checked
<% end %>
<% end %>
I honestly know that this isn't functional as is. I hope the insight about the paramters help, but without access to an actual rails console, I doubt I can create code that accomplishes what you are looking for. Here's a helpful link: Site point does Complex nested queries

How to access nested child element in fields_for

I'm trying to access a Hash type of mongoid in fieds_for and I already have a relationship with a model and want to access a hash of that model. Something like:
class Leave
field :leaves_types, :type => Hash
end
class User
has_many :leaves
end
Want to do something like:
form_for #user do |f|
f.fields_for :leaves.leave_types...
How I can achieve this? Thanks in advance.
You should give a block to fields_for. For more information on that method see docs. In your case, first, add this line to your User model:
class User
has_many :leaves
accepts_nested_attributes_for :leaves
end
This is required so that when your form is posted, the attributes coming from the form fields for leaves via params were handled correctly.
Now your template should look like this (for simplicity by now I assume that your Leave also has a simple text field named foo):
<%= form_for #user do |f| %>
...
<%= f.fields_for :leaves do |leave_fields| %>
# Fields for a leave here ----v
Foo: <%= leaves_fields.text_field :foo %>
<% end %>
<% end %>
Or, if you #user.leaves already initialized and you want form builder to put its values to form fields, you have to iterate over #user.leaves, passing each of them to fields_for as second argument:
<%= form_for #user do |f| %>
...
<% #user.leaves.each do |leave| %>
<%= f.fields_for :leaves, leave do |leave_fields| %>
# Note the addition here --^
Foo: <%= leave_fields.text_field :foo %>
<% end %>
<% end %>
<% end %>
But your question has another one inside: you have not a text field, but a hash, and there is no default form input for it (i.e. there is no f.hash_field :leaves_types). So you may want to make it by yourself like suggested in these questions: [1], [2] and [3].
Anyway, having a Hash field seems rather uncommon to me, so maybe Hash can be somehow replaced, say, with another has_many association (not sure), and in this case you will only need another nested fields_for.

Many-to-many associations and REST?

Newbie question, you've been warned!
I'm trying to implement a sample Rails app with a many-to-many association, people owning movies, and I'm trying to figure out how exactly to implement the UI for it. It's my understanding that REST requires everything to be a resource, so in this case "User" (person), "Movie" and "Possession" (the joint table) (oh, the puns).
Now the interesting part, the UX. Let's say I have a user dashboard where all of your movies are listed.
Let's say the user wants to add a movie that he owns. How do you do this in REST? It's trivial with a custom action that one could add to the User controller, but the point is not to go beyond the basic 7 REST actions, right? Therefore I'd have to first do a "new" on a movie and then do a "new" on a possession, which are two operations. How do I collapse them into one?
Basically I feel I'm not quite understanding how to maintain REST as soon as multiple models are involved and would appreciate a tip.
Thanks!
Happily, Rails has some magic just for this common scenario. Assuming a model like this:
class Movie
has_many :users, :through => :possessions
end
Your view:
<%= form_for [current_user, Movie.new] do |f| %>
<%= f.label :title %>
<%= f.text_field :title %>
<% end %>
Basically this form will POST to MoviesController#create and will pass along current_user.id as a user_id parameter that (the default) MoviesController#create will know to associate with the Movie it creates. Take a look at the documentation for FormBuilder#form_for for more information.
You could also do this the other way around, by the way:
class User
has_many :movies, :through => :possessions
accepts_nested_attributes_for :movies # magic!
end
And the view:
<%= form_for current_user |user_form| %>
<%= user_form.fields_for current_user.movies.build |movie_fields| %>
<%= movie_fields.label :title %>
<%= movie_fields.text_field :title %>
<% end %>
<% end %>
In this case the form will submit to UsersController#update and its parameters will look like this:
{ :id => 123,
:movie => {
:title => "The Red Balloon"
}
}
...and the controller will know to create the Movie object. For more information check the documentation for FormHelper#fields_for.

Rails: fields_for only one object

I've got a model Product in my Rails application, its attributes can be edited, and I want to let user comment every change he makes (a comment can be blank, though). So, Product has_many :comments, it accepts_nested_attributes_for :comments and rejects it if the comment is blank.
Hence, the edit form for Product is a multi-model form. The problems I faced are:
Fields_for helper renders text areas for all comments belonged to the product, so the user can edit all previous comments. I need it to render fields for the new one only.
If validation breaks, and there are no comments, fields_for renders nothing. Should I perform #product.comments.build in the view before fields_for statement every time, or there is more elegant way to do it?
Maybe I'm wrong and fields_for isn't suitable in this situation?
Base on Tots answer I just made it a little simplier (Rails 3 compatible):
<%= f.fields_for :comments, #product.comments.build do |comment| %>
<%= comment.label :comments %><br />
<%= comment.text_area :content %>
<% end %>
<% f.fields_for(:comments, Product.reflect_on_association(:comments).klass.new)
do |builder| %>
<%= builder.label :comment %>
<%= builder.text_area :comment, :rows => 3 %>
<% end %>

Save other model's fields from a certain view

I'm really new at Ruby On Rails and I'm working with a baseApp that comes with the login and user registration functionalities. It also creates an empty profile for a user when they sign up.
Then, I've created a migration to add fields to the profiles table, such as firstName, gender, state, country, etc.
What I'd like to do, is to show those fields at the registration form and then save them on the profiles table. How can I do this?
Thanks,
Brian
accepts_nested_attributes_for provides everything you need.
So long as you've st up your user to have one profile you're just about good to go.
Add accepts_nested_attributes_for :profile to the User model. Then set up your form to be something like this:
<% form_for :user do |f| %>
...
User model fields
...
<% #user.build_profile if #user.profile.nil? %>
<% f.fields_for :profile do |p| %>
<%= p.label :first_name %>
<%= p.text_field :first_name %>
...
Profile model fields
...
<% end %>
<% end %>
The controller needs no modification.
Read up on the nested form section of FormHelper for more details.

Resources