Formtastic nested form field not building has_one association? - ruby-on-rails-3

Given a User who can possibly be an Artist:
class User < ActiveRecord::Base
has_one :artist
end
I've got a User & Artist nested form (using Formtastic gem):
<h1>Artist registration</h1>
<% #user.build_artist unless #user.artist %>
<%= semantic_form_for #user, :url => create_artist_path do |f| %>
<%= f.inputs :username %>
<%= f.semantic_fields_for :artist do |a| %>
<%= a.input :bio %>
<% end %>
<%= f.buttons do %>
<%= f.commit_button 'Register as Artist' %>
<% end %>
<% end %>
The problem is the :artist fields are not rendered.
I've also tried f.inputs :for => :artist do |a|.
For some reason, using #user.build_artist does not display the artist's fields in the form. If I try #user.artist = Artist.new I get an error, because it tries to save the Artist and validation fails.
How should I initialize the Artist model so I get the benefit of formtastic generators in a nested form? (Note that #user here is not a :new_record?)

Did you remember to set accepts_nested_attributes_for :artist in user.rb?

Related

Mass assignment error in nested form in Rails

transaction.rb model:
class Transaction < ActiveRecord::Base
attr_accessible :customer, :tickets_attributes
has_many :tickets
accepts_nested_attributes_for :tickets
end
ticket.rb model:
class Ticket < ActiveRecord::Base
attr_accessible :booking_id, :quantity, :transaction_id
belongs_to :transaction
belongs_to :booking
end
in the view page i have a nested rails form for multiple entries of ticket:
<%= form_for(#transaction) do |f| %>
<%= f.text_field :customer %>
<% #sezzion.bookings.each do |booking| %>
<%= booking.bookingdate %>:
<%= f.fields_for :ticket do |t| %>
<%= t.text_field :quantity, :value => 0, :class => "quantity" %>
<%= t.hidden_field :booking_id, :value => booking.id %>
<% end %>
<% end %>
<%= f.submit "create transaction" %>
<% end %>
When I'm submitting the form, I have the following error:
ActiveModel::MassAssignmentSecurity::Error in TransactionsController#create
Can't mass-assign protected attributes: ticket
I have attr_accessible :tickets_attributes and accepts_nested_attributes_for :tickets in the transaction model and there's still an error. Also when I add plural to the ticket on line <%= f.fields_for :ticket do |t| %>, the quantity field doesn't display.
Your form f is based off of a Transaction object, which has_many :tickets. I believe you should be using the plural :tickets rather than the singular :ticket in your fields_for.
<%= f.fields_for :tickets do |t| %>
If you always want a new ticket, you may need to do:
<%= f.fields_for :tickets, Ticket.new do |t| %>
to ensure that a create form shows up.
total re-edit -- sorry its been a while I had to refresh my memory
transaction.rb tickets_attributes is ok.
class Transaction < ActiveRecord::Base
attr_accessible :customer, :tickets_attributes
has_many :tickets
accepts_nested_attributes_for :tickets
end
transaction_controller.rb you must build the tickets.
def new
#transaction = Transaction.new
#transaction.tickets.build
end
new.rb or in your form, fields_for must be for :tickets as rob as pointed out:
<%= form_for(#transaction) do |f| %>
...
<%= f.fields_for :tickets do |t| %>
...
I think you might be missing the build part in the controller. hope that helps!

HABTM Checkboxes using simpleform gem

I have three models, and i am trying to save onto a third table using simple form gem and checkboxes.
class Work < ActiveRecord::Base
has_many :skills, through: :skillships
end
The second model is
class Skill < ActiveRecord::Base
has_many :works, through: :skillships
end
The third is
class Skillship < ActiveRecord::Base
belongs_to :work
belongs_to :skill
end
Using the Work model i am trying to save the data on the skillship table. Something similar to this http://railscasts.com/episodes/17-habtm-checkboxes-revised. Can you please help.
EDIT
The form
<%= simple_form_for(#work) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :title, :label => 'Project Title' %>
<%= f.input :excerpt, :as => :text %>
<fieldset>
<legend>Skills Used </legend>
Would like to check the skills i used here.
</fieldset>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
I tried..
<%= hidden_field_tag "work[skill_ids][]", nil %>
<% Skill.all.each do |skill| %>
<%= check_box_tag "work[skill_ids][]", skill.id, #work.skill_ids.include?(skill.id), id: dom_id(skill) %>
<%= label_tag dom_id(skill), skill.title %><br>
<% end %>
The reason i'm doing this it because work can have many skills used.
I was going about this the wrong way. A join table solved the problem.
rails g migration create_skills_works_table
Then
class CreateSkillsWorksTable < ActiveRecord::Migration
def self.up
create_table :skills_works, :id => false do |t|
t.references :skill
t.references :work
end
add_index :skills_works, [:skill_id, :work_id]
add_index :skills_works, [:work_id, :skill_id]
end
def self.down
drop_table :skills_works
end
end
Using simple form on the work view.
<fieldset>
<legend>Skills Used </legend>
<%= f.association :skills %>
</fieldset>

Rails nested form with multiple entries

I have a Sezzion model:
attr_accessible :description
has_many :session_instructors, :dependent => :destroy
has_many :instructors, :through => :session_instructors
accepts_nested_attributes_for :session_instructors
accepts_nested_attributes_for :instructors
Instructor model:
attr_accessible :bio
has_many :sezzions, :through => :session_instructors
has_many :session_instructors, :dependent => :destroy
SessionInstructor model:
attr_accessible :instructor_id, :sezzion_id
belongs_to :sezzion
belongs_to :instructor
Lastly, User model:
has_many :sezzions
has_many :instructors
I'm trying to create a form for Sezzion with nested form for SessionInstructor which has multiple select option for Instructors.
How can I do the following:
nested form for SessionInstructor
use multiple select option to get all the selected Instructor's instructor_id
hidden field to pass in the created/updated session_id with each select instructor
I have the following code as of now:
<%= form_for(#sezzion) do |f| %>
<%= f.label :description %>
<%= f.text_area :description %>
<%= f.label :instructors %>
<%= fields_for :session_instructors do |f| %>
<select multiple>
<% current_user.instructors.each do |instructor| %>
<option><%= instructor.name %></option>
<% end %>
</select>
<% end %>
<%= f.submit %>
<% end %>
Thank you so much!
This is something that seems ridiculously hard in Rails.
I think something like this might work:
<%= f.fields_for :session_instructors do |si| %>
<%= si.collection_select :instructor_ids, current_user.instructors, :id, :name, multiple: true>
<% end %>
This should create a form element which will set sezzion[session_instructors_attributes][instructor_ids].
Although I'm not sure if that's actually what you want. I've never tried this using a multi select. If it doesn't work, you could also try getting rid of the fields_for and just using f.collection_select. If you're willing to use a checkbox, I can show you how to do that for sure.
I hope that helps.
Edit:
Here's how I would usually do it with a check_box:
<%= f.fields_for :session_instructors do |si| %>
<%= si.hidden_field "instructor_ids[]" %>
<% current_user.instructors.each do |i| %>
<%= si.check_box "instructor_ids[]", i.id, i.sezzions.include?(#sezzion), id: "instructor_ids_#{i.id}" %>
<%= label_tag "instructor_ids_#{i.id}", i.name %>
<% end %>
<% end%>
There are a couple "gotchas!" with this method. When editing a model, if you deselect all checkboxes then it won't send the parameter at all. That's why the hidden_field is necessary. Also, you need to make sure each form element has a unique id field. Otherwise only the last entry is sent. That's why I manually set the value myself.
I copy pasted and then edited. Hopefully I got the syntax close enough where you can get it to work.
FINAL EDIT:
Per Sayanee's comment below, the answer was a bit simpler than I thought:
<%= f.collection_select :instructor_ids, current_user.instructors, :id, :name, {}, {:multiple => true} %>
#Sayanee, can you post how your instructors, sezzions table look like. Also for note, you can not get instructor_ids from Instructor object, hence you are getting "undefined method" error. With the current association that you shared, you can get instructor_ids from a Sezzion object. So you need to loop through current_user.sezzions in stead of current_user.instructors.
This is a way to implement fields_for nested form with html multiple_select in case of a has_many :through association. Solved it by doing something like this. The form view:
<%= form_for(#sezzion) do |f| %>
...
<%= fields_for :session_instructors do |g| %>
<%= g.label :instructor, "Instructees List (Ctrl+Click to select multiple)" %>:
<%= g.select(:instructor_id, Instructor.all.collect { |m| [m.name, m.id] }, {}, { :multiple => true, :size => 5 }) %>
<%= g.hidden_field :your_chosen_variable_id, value: your_chosen.id %>
<% end %>
...
<%= f.submit %>
<% end %>
Note:Since the #sezzion would not be saved at the time of generating the form you cannot pass that id (#sezzion.id) in place of your_chosen.id through the form. You could handle that save in the controller.
Make sure that your controller Initializes the Variables while generating the form: Your def new could look something like this:
def new
#sezzion = Sezzion.new
#sezzion.session_instructor.build
#sezzion.instructors.build
end
Now the create controller has to be able to accept the strong params required for the multiple select, so the sezzion_params method may look something like this:
def sezzion_params
params.require(:sezzion).permit(:description, :any_other_fields,
:session_instructors_attributes =>
[:instructor_id => [], :your_chosen_id => Integer])
end
In the create function, the first session_instructor variable is linked to the #sezzion instance variable through our new function. The other session_instructors in our multiple select must be built after the Sezzion instance is saved, if you want to pass in the created #sezzion.id with each select instructor. .
def create
#sezzion = Sezzion.new(sezzion_params)
#startcount=1 #The first element of the array passed back from the form with multiple select is blank
#sezzion.session_instructors.each do |m|
m.instructor_id = sezzion_params["session_instructors_attributes"]["0"]["instructor_id"][#startcount]
m.your_chosen_variable_id = your_chosen.id
#startcount +=1
end
if #sezzion.save
sezzion_params["session_instructors_attributes"]["0"]["instructor_id"].drop(#startcount).each do |m|
#sezzion.session_instructors.build(:instructor_id => sezzion_params["session_instructors_attributes"]["0"]["instructor_id"][#startcount],
:your_chosen_variable_id => your_chosen.id).save
#startcount += 1
end
flash[:success] = "Sezzion created!"
redirect_to root_url
else
flash[:danger] = "There were errors in your submission"
redirect_to new_sezzion_path
end
end

fields_for for has_many through relationship rails 3

I have problem in getting below code to work.
class Page < ActiveRecord::Base
has_many :page_parts, :through => :page_parts_pages
has_many :page_parts_pages
accepts_nested_attributes_for :page_parts, :allow_destroy => true
accepts_nested_attributes_for :page_parts_pages, :allow_destroy => true
end
class PagePart < ActiveRecord::Base
has_many :page_parts_pages
has_many :pages, :through => :page_parts_pages
end
class PagePartsPage < ActiveRecord::Base
belongs_to :page
belongs_to :page_part
end
Table Structure:-
pages
id, title
pages_parts
id, title
page_parts_pages
id, page_id, page_part_id
View code
<% page_fragment.each do |k,v| %>
<% if v.nil? or v.blank? or v.empty? %>
<% parts = f.object.page_parts.build if f.object.page_parts.blank? %>
<%= f.fields_for :page_parts, parts do |p| %>
<%= render 'page_part_form_field', :f => p %>
<% end %>
<% else %>
<% parts_page = f.object.page_parts_pages.build if f.object.page_parts_pages.blank? %>
<%= f.fields_for :page_parts_pages, parts_page do |p| %>
<%= render 'page_part_page_form_field', :f => p %>
<% end %>
<% end %>
<% end %>
Actually the scenario is, I have to display the fields for page_parts and page_parts_pages on condition basis. If condition is satisfied, display fields for page_parts else display fields for page_parts_pages.
It's working perfectly fine for new action but for edit action it is not displaying correctly.
Any help is highly appreciated.
Thanks in advance
You are creating new page_parts in this form:
parts = f.object.page_parts.build if f.object.page_parts.blank?
parts_page = f.object.page_parts_pages.build if f.object.page_parts_pages.blank?
'build' creates new objects (it will not persist them in database though). So, no wonder it works for new, but not for edit.
You can try this:
<% page_fragment.each do |k,v| %>
<% if v.blank? %>
<%= f.fields_for :page_parts do |p| %>
<%= render 'page_part_form_field', :f => p %>
<% end %>
<% else %>
<%= f.fields_for :page_parts_pages do |p| %>
<%= render 'page_part_page_form_field', :f => p %>
<% end %>
<% end %>
<% end %>
rails api has pretty good documentation of forms_for and other form helpers.

Problem using nested_form gem when save data

It's my first time here, and first time I use nested_form gem. Everything seemed to be ok, but the data from my "parent" model doesn't save.
Here is my code
<%= nested_form_for #project do |f| %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<%= f.fields_for :tasks %>
<p><%= f.link_to_add "Add a task", :tasks %></p>
<%= f.submit %>
<% end %>
so, when I "submit", just the tasks are saved ok, but not the project name.
Any clue for me? did I miss something??
You need to add the attribute. eg name to attr_accessible.
# app/models/project.rb
class Project < ActiveRecord::Base
has_many :tasks, :dependent => :destroy
accepts_nested_attributes_for :tasks, :allow_destroy => true
attr_accessible :name,:tasks_attributes ## <-- you need this line
end
Your fields_for declaration isn't quite right
<%= f.fields_for :tasks %>
Should be
<%= f.fields_for :tasks do |task_builder| %>
you are also missing an end for that declaration and a render to render the partial that has the nested fields for the associated object.
So you should end up with something like this
<%= f.fields_for :tasks do |task_builder| %>
<%= render 'task_fields', :f => task_builder %>
<% end %>
<p><%= f.link_to_add "Add a task", :tasks %></p>
That should do the trick. all you need to do now is create a _task_field.html.erb partial and add the task fields to it in the usual way using f.label, f.text_field etc...
p.s.
Your code could not possibly have ever worked. You would have had errors so something is probably missing from your opening post.