accepts_nested_attributes_for - belongs_to, has_many, fields_for - ruby-on-rails

Is there any possible way to use nested_attributes_for in the way show below?
Basically I want to create a person, one or more cars and add details to each car. This is just a mock up, not a very realistic example. I get snagged when trying to build the details for the car as it hasn't been created yet.
Models:
class Person < ActiveRecord::Base
has_many :cars
accepts_nested_attributes_for :car
end
class Car < ActiveRecord::Base
belongs_to :person
has_many :details
accepts_nested_attributes_for :details
end
class Detail < ActiveRecord::Base
belongs_to :car
end
Form:
form_for #person do |f|
#fields
f.fields_for :car do |car|
#fields
car.fields_for :details |detail|
=detail.text_field :content
end
end
end

Have a look at that http://railscasts.com/episodes/196-nested-model-form-part-1?view=asciicast

Related

Solution for nested form

I have been stuck on this problem for a while.
Need to make a form for competitions category with custom inputs. It should take all values from Information table and build the inputs, but the tricky part is that it should be saved to Category_informations table.
class Competition < ApplicationRecord
has_many :categories
has_many :informations
end
class Category < ApplicationRecord
belongs_to :competetion
has_many :category_informations
has_many :information, through: competition
end
class CategoryInformation
belongs_to :catagory
belongs_to :information
end
class Information < ApplicationRecord
belongs_to :competetion
has_many :category_informations
end
Competition -> name
Category -> name, competition_id
Information -> name, competition_id
Category_informations -> value, category_id, information_id
Take a look at this gem: https://github.com/plataformatec/simple_form
Simple Form aims to be as flexible as possible while helping you with powerful components to create your forms.
Let's take a simple example:
class Machine < ActiveRecord::Base
has_many :parts , inverse_of: :machine
accepts_nested_attributes_for :parts
end
class Part < ActiveRecord::Base
# name:string
belongs_to :machine
end
With these models, we can use simple_form to update the machine and its associated parts in a single form:
<%= simple_form_for #machine do |m| %>
<%= m.simple_fields_for :parts do |p| %>
<%= p.input :name %>
<% end %>
<% end %>
For 'new' action, build the nested model from the controller:
class MachinesController < ApplicationController
def new
#machine = Machine.new
#machine.parts.build
end
end
Source: https://github.com/plataformatec/simple_form/wiki/Nested-Models
Sounds to me like you're looking for accepts_nested_attributes_for
See:
https://apidock.com/rails/v3.2.3/ActiveRecord/NestedAttributes/ClassMethods/accepts_nested_attributes_for
https://rubyplus.com/articles/3681-Complex-Forms-in-Rails-5
Also, check out the cocoon gem.

Rails - Issue in has_many through with nested attributes

I am having issue with saving a has_many through relation with nested attributes. Due to complexity and requirment in the application the relation is as follows
Table structure,
agreements:
id
agreement_rooms:
id
agreement_id
room_id
details:
id
agreement_rooms_id
For more clarification, agreement_rooms table is related to many other models which will be having agreement_rooms_id in them.
Rails Associations,
class Agreement < ActiveRecord::Base
has_many :details,:through => :agreement_rooms
accepts_nested_attributes_for :details
end
class AgreementRoom < ActiveRecord::Base
has_many :details
end
class Detail < ActiveRecord::Base
belongs_to :agreement_room
accepts_nested_attributes_for :agreement_room
end
When i try to create a agreements record with details hash in it, i get the following error,
Agreement.last.details.create()
ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection: Cannot modify association 'agreement#details' because the source reflection class 'Detail' is associated to 'agreementRoom' via :has_many.
I am not sure how to get this nested attributed working with has_many through relation for the above example. Please help out to figure the issue.
Thanks in advance.
#app/models/aggreement.rb
class Agreement < ActiveRecord::Base
has_many :agreement_rooms
accepts_nested_attributes_for :agreement_rooms
end
#app/models/agreement_room.rb
class AgreementRoom < ActiveRecord::Base
belongs_to :agreement
belongs_to :room
has_many :details
accepts_nested_attributes_for :details
end
#app/models/room.rb
class Room < ActiveRecord::Base
has_many :agreement_rooms
has_many :agreements, through: :agreement_rooms
end
#app/models/detail.rb
class Detail < ActiveRecord::Base
belongs_to :agreement_room
end
--
#app/controllers/agreements_controller.rb
class AgreementsController < ApplicationController
def new
#agreement = Agreement.new
#agreement.agreement_rooms.build.details.build
end
def create
#agreement = Agreement.new agreement_params
#agreement.save
end
private
def agreement_params
params.require(:agreement).permit(:agreement, :param, agreement_rooms_attributes: [ details_attributes: [:x] ])
end
end
#app/views/agreements/new.html.erb
<%= form_for #agreement do |f| %>
<%= f.fields_for :agreement_rooms do |ar| %>
<%= ar.fields_for :details do |d| %>
<%= d.text_field :x %>
<% end %>
<% end %>
<%= f.submit %>
<% end %>
you need to define both associations:
class Agreement < ActiveRecord::Base
has_and_belongs_to_many :agreement_rooms # or has_many if you prefer
has_many :details,:through => :agreement_rooms
accepts_nested_attributes_for :details
end
check the docs
As i said before the model association design we has was not proper and due to poor maintenance it has to be in the same way, atleast for now. So i had to write a dirty patch to fix it.
Its simply skipping nested attributes for this specific model alone, so it can be saved separately by passing the master record id to this record.
As its a dirty solution i'm not marking it as the answer. Just added it hoping someone can have a solution if needed.
Thanks for the help

uninitialized constant error when joining 3 tables in view

I have 3 tables. pin, genre and genres_pins.
genres_pins joins the pin and genre tables together with a many to many. Here's my setup:
Pin Model
class Pin < ActiveRecord::Base
belongs_to :user
belongs_to :type
has_many :replies
has_many :genres_pins
has_many :genres, :through => :genres_pins
end
Genre Model
class Genre < ActiveRecord::Base
has_many :genres_pins
has_many :pins, :through => :genres_pins
end
GenresPins Model
class GenresPins < ActiveRecord::Base
belongs_to :pin
belongs_to :genre
end
View
<% pin.genres_pins.each do |g| %>
<%= g.title %>
<% end %>
I get the following error:
uninitialized constant Pin::GenresPin
Any idea what's going on here? I'm new to Rails, so may be missing something stupidly obvious.
Help appreciated.
Many thanks,
Michael.
class GenrePin < ActiveRecord::Base
belongs_to :pin
belongs_to :genre
end
The name of the class should be changed

Searching one model by another

My application consists of a drink model
class Drink < ActiveRecord::Base
attr_accessible :name
has_many :recipe_steps, :dependent => :destroy
has_many :ingredients, through: :recipe_steps
end
An ingredient model
class Ingredient < ActiveRecord::Base
attr_accessible :name
has_many :recipe_steps
end
how would I go about having it so when a user searches an ingredient that it returns all of the drinks with that ingredient?
Additional information: I'm currently using sunspot/solr for my searching.
First, in your Ingredient model you'd need this line:
has_many :drinks, through: :recipe_steps
To define the has_many, through: relationship. Make sure that RecipeStep has these lines, too:
belongs_to :ingredient
belongs_to :drink
Then you can do something like in the DrinksController:
def search
term = params[:search]
ingredient = Ingredient.where(:name => term)
#drinks = Ingredient.find(ingredient).drinks
end
And your form should look something like this:
<%= form_for #drink, :url => { :action => "search" } do |f| %>
<%= f.text_field :search %>
<% end %>
I don't know all your names for everything but this should get you going.
Following should work fine:
class Ingredient < ActiveRecord::Base
...
has_many :recipe_steps
has_many :drinks, through: :recipe_steps
end

collection_select with has_many :through relationship table data

I have such terrible models:
class ParentalRelation < ActiveRecord::Base
belongs_to :parent
belongs_to :student
belongs_to :counselor
belongs_to :parental_relation_type
end
class ParentalRelationType < ActiveRecord::Base
has_many :parental_relations
end
class Parent < ActiveRecord::Base
has_many :parental_relations
has_many :students, :through => :parental_relations
has_many :counselors, :through=> :parental_relations
has_many :parental_relation_types, :through=> :parental_relations
belongs_to :user, :dependent=> :destroy
belongs_to :occupation_type
accepts_nested_attributes_for :user
end
Parental relation types are like father, mother, etc. The reasoning is that a parental relation between one counselor, one parent and one student is unique and counselors should not see the relations that belong other counselors.
In controllers/parent_controller/edit action I have:
#parental_relation= ParentalRelation.find_by_counselor_id_and_student_id_and_parent_id(x, y, z)
In views/parent/_form.html.erb I have:
<%= form_for #parent do |f| %>
inside that form I need a collection_select for ParentalRelationType.all and select the parent's parental_relation_type_id for that particular parental relation, but I can't find the syntax to do it.
I tried adding
<%= collection_select(#parental_relation, :parental_relation_type_id, ParentalRelationType.all, :id, :name) %>
underneath the form_for, but the relation type id is 2, and default 1 is selected instead.
Added this to parents/_form
<%= fields_for #counselor_student_parent do |csp| %>
<%= f.label :parental_relation_type_id %>
<%= collection_select(:student_counselor_parent, :parental_relation_type_id, ParentalRelationType.all, :id, :name) %>
<% end %>
And this to parents_controller/new
def new
#counselor= Counselor.find(params[:counselor_id])
#student= Student.find(params[:student_id])
#parent= #student.parents.build
#parent_user= #parent.build_user
#counselor_student_parent= #counselor.student_counselor_parents.build
end

Resources