Nested Forms and Building associations - ruby-on-rails

I'm working on a add to cart form. It looks something like this
#Models
Order.rb
has_many :line_items
accepts_nested_attributes_for :line_items, :allow_destroy => true
LineItem.rb
has_one :product
belongs_to :order
Product.rb
belongs_to :line_item
I'd like to create a form in product#show to allow multiple related products to be added to the order/cart at once, basically create or update a line item for each product.
Probably something like this in the view (HAML to keep it brief).
-form_for #order do |f|
- if has_related?
- for related in #products.related_products
- f.field_for :line_item do |li_form|
= li_form.text_field :quantity
= li_form.hidden_field :product_id
= related.product_name
What would it take to actually make something like this work?

I would need more info to be sure, but it seems that a LineItem belongs_to :product and Product should NOT belong_to :line_item unless there really is a 1-1 relationship there (which wouldn't make sense to me, and doesn't follow the normal convention of these sorts of systems)
Note** using - before form_for and fields_for has been deprecated in rails 3 in favor of = since the form does actually render html
= fields_for :line_items do |li_form| is the syntax for a has_many relationship
The rest all depends on your user experience design.
Hope this helps!

Related

Rails 3 Polymorphic nested attributes

I am trying to build a polymorphic relationship from a nested form that's backwards to all the examples I've found. I am hoping someone to point out the error of my ways.
class Container < ActiveRecord::Base
belongs_to :content, :polymorphic => true
end
class Notice < ActiveRecord::Base
has_one :container, :as => :content
end
class Form < ActiveRecord::Base
has_one :container, :as => :content
end
It seems most people would build a Container from a Notice or Form, but in my case the notice or form contains a small amount of content (file location or a couple db fields) so it's much dry'er to build the Notice or Form from the Container.
I thought I could solve by adding accepts_nested_attributes_for :content but that gives me an unrecognized attribute :notice when I try to create a Container with a nested Notice (looking for content, not the polymorphic association)
I can do it manually and explicitly exclude the nested fields like
if params[:container].has_key('notice')
#c = Container.new(params[:container].except(:notice))
and then build, but isn't that a smell? Is there a better way?
Thank you for reading!
Nested attributes are designed to work from the parent down to the children, not the other way around. Moreover, in this scenario, how would nested attributes know whether you are trying to create a Notice or Form object?
If you find it DRYer to build the content from the container, you probably have your associations inside out - try changing your schema to:
class Container < ActiveRecord::Base
has_one :notice
has_one :form
end
class Notice < ActiveRecord::Base
belongs_to :container
end
class Form < ActiveRecord::Base
belongs_to :container
end
You can use validation to ensure only one child (:notice or :form) is actually associated if need be.

creating complex nested forms with cocoon

I've created a complex nested form, but I'm having trouble with the part where my app creates/edits records based on the input from that form correctly.
To-Do scenario: I need to enter Tasks one-by-one into my form (one task per "submit" press), from a pile of little pieces of papers with tasks written on them. Each Task has a :project_number, a :project_number_type, and an :owner written on it. In my app, these attributes are in the models ProjectNumber (for the first two) and Owner, for the last one. (I had to make a separate ProjectNumber model because my Projects can at times have multiple :project_numbers of different project_number_types.)
class Owner < ActiveRecord::Base
has_many :projects
has_many :tasks, :through => :projects
end
class Project < ActiveRecord::Base
belongs_to :owner
has_many :tasks
has_many :project_numbers
end
class ProjectNumber < ActiveRecord::Base
belongs_to :project
end
class Task < ActiveRecord::Base
belongs_to :project
delegate :owner, :to => :project, :allow_nil => true
end
My form shows the correct nested fields_for fields for each of the attributes mentioned above. (I omitted things like my accepts_nested_attributes_for).
2 related (I think) issues:
My form never creates any new projects! The Cocoon gem I'm using to create nested forms has me creating form partials for each nested model. In the Project partial, since my Project model doesn't have a :number attribute itself, but instead has an association to the ProjectNumbers model which does, I just have a reference to the ProjectNumbers partial and its attributes.
I need my app to edit an existing Project with the new Task if the Task's :project_number, :project_number_type, and :owner all match those associated with an existing Project. Otherwise, it should create a new Project for that Task to belong to.
I read lots of little bits of code here and elsewhere, but I need something just a little more, maybe that includes the "what" as in .first_or_initialize and also the "where", like as in /models/task.rb, or tasks_controller.rb? Maybe I need to create something custom? Thanks so much!
Edit: Here are the form partials
==== /views/tasks/_form.html.slim
= simple_form_for #task do |f|
#project
= f.simple_fields_for :project do |project|
= render 'project_fields', :f => project
==== /views/tasks/_project_fields.html.slim
.nested-fields
.project_numbers
= f.simple_fields_for :project_numbers do |project_number|
= render 'project_number_fields', :f => project_number

Trouble with Rails 3 has_many :through association and views/forms

I have previously used has_and_belongs_to_many associations in my older Rails apps but am moving to using has_many, :through for new and current apps. However, I believe I am missing something central to the has_many, :through association in Rails 3.
Currently, I am building an app for our town's volunteer fire department. When we have a meeting, we want to check off whether or not a fire fighter is present, excused, or absent.
My models:
#FireFighter Model
class FireFighter < ActiveRecord::Base
has_many :attendance_meetings
has_many :meetings, :through => :attendance_meetings
accepts_nested_attributes_for :meeting
#Meeting Model
class Meeting < ActiveRecord::Base
has_many :attendance_meetings
has_many :fire_fighters, :through => :attendance_meetings
accepts_nested_attributes_for :fire_fighter
#AttendanceMeeting Model
class AttendanceMeeting < ActiveRecord::Base
attr_accessor :status # this is the added property on the join model that we need populated
belongs_to :fire_fighter
belongs_to :meeting
The problem I'm having is how to set this up in the view. Currently, I have:
= hidden_field_tag "meeting[fire_fighter_ids][]"
-#meeting.fire_fighters.each do |fire_fighter|
= fire_fighter.fire_fighter_name
%span.radio_btn= radio_button_tag :fire_figher_ids, fire_fighter.id, "present"
present
%span.radio_btn= radio_button_tag :fire_figher_ids, fire_fighter.id, "excused"
excused
%span.radio_btn= radio_button_tag :fire_figher_ids, fire_fighter.id, "absent"
absent
%br
This will spit out each fire fighter and three radio buttons for each fire fighter (with options for present, excused, or absent). However, the name of the resulting radio buttons are all the same, so you can only pick one for all fire fighters.
As I noted above, I feel certain I am missing something basic but I am stumped. I've read through tons of SO questions and The Rails 3 Way book's sections on ActiveRecord. Any suggestions or directions would be most appreciated. Thank you!
It should be something like:
class Meeting < ActiveRecord::Base
has_many :attendance_meetings
has_many :fire_fighters, :through => :attendance_meetings
accepts_nested_attributes_for :attendance_meetings
# view
= form_for #meeting do |f|
= f.fields_for :attendance_meetings do |f_a_m|
= f_a_m.object.fire_fighter.name
= f_a_m.check_box :status, 'present'
= f_a_m.check_box :status, 'excused'
= f_a_m.check_box :status, 'absent'
For the approach you're taking you need to build the association for each firefighter with the meeting. Something like:
#meeting.firefighters << Firefighter.all
This doesn't seem particularly optimal however. I would form the join with a boolean :status for those who are absent (t) or excused (f) with present not included, imagine it like meeting_absentees. Seems backward but in this way your table will have far fewer rows.

Nested form - how to add existing nested object to parent form?

I've got my models setup for a many-to-many relationship:
class Workshop < ActiveRecord::Base
has_many :workshop_students
has_many :students, :through => :student_workshops
accepts_nested_attributes_for :students
end
class Student < ActiveRecord::Base
has_many :student_workshops
has_many :workshops, :through => :student_workshops
accepts_nested_attributes_for :products
end
class StudentWorkshop < ActiveRecord::Base
belongs_to :student
belongs_to :workshop
end
As you can see above, a student can have many workshops and workshop can have many students.
I've looked at the following Rails casts: here and here. And most of the online sources I stumble across only show how to do nested forms for creating new objects within the parent form.
I don't want to create a new object. I only want to add an existing object to the parent form. So for example. If I decide to create a new workshop, I'd like to assign existing students to the workshop.
One thing I don't understand is, how do I link students into the workshop form? Second, when the params are passed, what should be in the controller method for update/create?
If anyone can point me to the right direction, I would appreciate it.
The easiest thing to do is:
<%= f.collection_select(:student_ids, Student.all, :id, :name, {:include_blank => true}, {:selected => #workshop.student_ids, :multiple => true} )%>
You should not have to do anything in the create action.
Ok, for anyone coming across the same issue in the future. The solution I came up with was in def create. I am able to access a POST attribute called student_ids, which comes in the form of an array

Rails 3: Having trouble with nested forms and has_many :through

Here's my model set up.
Band Model
has_many :bands_genres
has_many :genres, :through => :bands_genres
Genre Model
has_many :bands_genres
has_many :bands, :through => :bands_genres
BandsGenre Model
belongs_to :band
belongs_to :genre
I have a form where you can add a new band and then select a genre from a dropdown field that pulls from the pre-set genres in the genre model.
So what I ultimately need to do is set up a form so that when a band adds their band and select a genre, it creates the correct join in the bands_genre model.
Not sure where to start with setting up the form, controllers and models for this.
I'm running Rails 3.0.3
There are quite a few text/video casts covering this, since its a popular use case. I would encourage you to look at:
http://railscasts.com/episodes/73-complex-forms-part-1 or its equivalent asciicast (which is a text based cast of the video).
Further I would recommend you use formtastic. Associations are managed automatically so it makes form building trivial and keeps your code tidy. And yes there are casts for that too.
http://railscasts.com/episodes/184-formtastic-part-1
Edit:
Band Model
has_many :genres, :as => :band_genres
Genre Model
has_many :bands, :as => :band_genres
Your genre table has a band_id, and your band table has a genre_id.
bands_controller
def new
#genres = Genre.all
#post = Post.new
end
posts/new.html.haml
(This part I'm a little unsure of, but it roughly goes like this)
- form_for #post do |f|
= f.select :genre_id, #genres, {}
= f.submit

Resources