Rails form, iterating over a collection - ruby-on-rails

I'm trying to iterate over a collection in a form, where I would like to ask the user to fill in a quantity for each individual extra_guest.
My current form indeed shows the collection as intended, but does not pass the extra_guest_id to reservation_extra_guest.
models
class Reservation < ApplicationRecord
has_many :reservation_extra_guests, dependent: :destroy
has_many :extra_guests, through: :reservation_extra_guests
accepts_nested_attributes_for :reservation_extra_guests
end
class ReservationExtraGuest < ApplicationRecord
belongs_to :extra_guest
belongs_to :reservation
accepts_nested_attributes_for :extra_guest
end
class ExtraGuest < ApplicationRecord
belongs_to :age_table
has_many :reservation_extra_guests
has_many :reservations, through: :reservation_extra_guests
end
controller
def new_second_part
#hotel = Hotel.find(params[:hotel_id])
#reservation = Reservation.find(params[:id])
#guests = []
#reservation.room.room_category.extra_guests.each do |guest|
res_guest = #reservation.reservation_extra_guests.build
res_guest[:extra_guest_id] = guest.id
#guests << res_guest
end
end
form
<%= simple_form_for [#hotel, #reservation] do |f|%>
<%= f.simple_fields_for :reservation_extra_guests do |g| %>
<p><%= g.object.extra_guest.age_table.name %>
<%= g.input :extra_guest_quantity, label: false, collection: 1..20 %></p>
<% end %>
<% end %>
params sent
"reservation"=>{"reservation_extra_guests_attributes"=>{"0"=>{"extra_guest_quantity"=>"3"}, "1"=>{"extra_guest_quantity"=>"4"}}

Related

Cannot access attributes from associated model rails

I have three models, ingredient, recipe_ingredient and recipy
class Ingredient < ApplicationRecord
has_many :recipe_ingredients
end
class RecipeIngredient < ApplicationRecord
belongs_to :recipy, :dependent => :destroy
belongs_to :ingredient
end
class Recipy < ApplicationRecord
has_many :recipy_steps
has_many :recipe_ingredients, :dependent => :delete_all
end
I am trying to access the ing_name attribute in the ingredients table from recipies show page.
<% #recipe_ingredients.each do |ing| %>
<p> <%= ing.amount %> <%= ing.unit %>
<%= ing.ingredient.ing_name %>
</p>
def Show from the recipies controller:
def show
#recipe_ingredients = #recipy.recipe_ingredients
end
But I keep receiving the following error msg:
undefined method `ing_name' for nil:NilClass
My ingredient_params:
def ingredient_params
params.require(:ingredient).permit(:ing_name)
end
It does seem to work like this:
<%= Ingredient.where(id: ing.ingredient_id).pluck(:ing_name) %>
But this does not use the connection between the tables if I understand correctly? Any help? Thanks.
You have ingredient nil thats why you got the error.
Must be your controller has some before_action hook to load recipy
class RecipesController < ApplicationController
before_action :load_recipy, only: :show
def show
#recipe_ingredients = #recipy.recipe_ingredients
end
private
def load_recipy
#recipy = Recipy.find(params[:id])
end
end
You can try this to avoid this nil error(undefined method 'ing_name' for nil:NilClass)
<% #recipe_ingredients.each do |ing| %>
<p> <%= ing.amount %> <%= ing.unit %>
<%= ing.try(:ingredient).try(:ing_name) %>
</p>
From Rails 5 by default you got one required option to make ingredient always not nullable
like below
belongs_to :ingredient, required: true
It will also prevent this error of
class RecipeIngredient < ApplicationRecord
belongs_to :recipy, :dependent => :destroy
belongs_to :ingredient, required: true
end
the problem is because inside your show method #recipy is nil,
here is usually code for show
controller
def show
#recipy = Recipy.find(params[:id]) # --> you missed this line
#recipe_ingredients = #recipy.recipe_ingredients # #recipy will be null without line above
end
view
<% #recipe_ingredients.each do |ing| %>
<p> <%= ing.amount %> <%= ing.unit %> <%= ing.ingredient.ing_name %> </p>
<% end %>
I would like also add some suggestion to your model relationship as follow since Ingredient and Recipy shows many to many relationship
class Ingredient < ApplicationRecord
# -> recipe_ingredients -> recipies
has_many :recipe_ingredients, :dependent => :destroy
has_many :recipies, through: :recipe_ingredients
end
class RecipeIngredient < ApplicationRecord
belongs_to :recipy
belongs_to :ingredient
end
class Recipy < ApplicationRecord
has_many :recipy_steps
# -> recipe_ingredients -> ingredients
has_many :recipe_ingredients, :dependent => :destroy
has_many :ingredients, through: :recipe_ingredients
end

Rails 5: Nested params not nested with has_many :through

I'm trying to create a form in Rails 5.2 for a model with a has_many :through relationship to another model. The form needs to include nested attributes for the other model. However, the params are not nesting properly. I've created the following minimal example.
Here are my models:
class Order < ApplicationRecord
has_many :component_orders, dependent: :restrict_with_exception
has_many :components, through: :component_orders
accepts_nested_attributes_for :components
end
class Component < ApplicationRecord
has_many :component_orders, dependent: :restrict_with_exception
has_many :orders, through: :component_orders
end
class ComponentOrder < ApplicationRecord
belongs_to :component
belongs_to :order
end
The Component and Order models each have one attribute: :name.
Here is my form code:
<%= form_with model: #order do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= fields_for :components do |builder| %>
<%= builder.label :name %>
<%= builder.text_field :name %>
<% end %>
<%= f.submit %>
<% end %>
When I fill out the form, I get the following params:
{"utf8"=>"✓", "authenticity_token"=>"ztA1D9MBp1IRPsiZnnSAIl2sEYjFeincKxivoq0/pUO+ptlcfi6VG+ibBnSREqGq3VzckyRfkQtkCTDqvnTDjg==", "order"=>{"name"=>"Hello"}, "components"=>{"name"=>"World"}, "commit"=>"Create Order", "controller"=>"orders", "action"=>"create"}
Specifically, note that instead of a param like this:
{
"order" => {
"name" => "Hello",
"components_attributes" => {
"0" => {"name" => "World"}
}
}
}
There are separate keys for "order" and "components" at the same level. How can I cause these attributes to nest properly? Thank you!
EDIT: Here is my controller code:
class OrdersController < ApplicationController
def new
#order = Order.new
end
def create
#order = Order.new(order_params)
if #order.save
render json: #order, status: :created
else
render :head, status: :unprocessable_entity
end
end
private
def order_params
params.require(:order).permit(:name, components_attributes: [:name])
end
end
You should include accepts_nested_attributes_for :components in the Order model.
class Order < ApplicationRecord
has_many :component_orders, dependent: :restrict_with_exception
has_many :components, through: :component_orders
accepts_nested_attributes_for :components
end
And change
<%= fields_for :components do |builder| %>
to
<%= f.fields_for :components do |builder| %>
to get the desired params. accepts_nested_attributes_for :components creates a method namely components_attributes
More Info here

Request parameters not saving on model_params on n:n relationship with check_box_tag

I am using Rails 5.1 and im having some issues saving params on an n:n relationship.
I have three models:
class Course < ApplicationRecord
belongs_to :studio
has_many :reviews, dependent: :destroy
has_many :has_category
has_many :categories, through: :has_category
validates :name, presence: true
end
class Category < ApplicationRecord
has_many :has_category
has_many :courses, through: :has_category
end
class HasCategory < ApplicationRecord
belongs_to :category
belongs_to :course
end
and a simple form to create a new course with different categories using check_box_tag (not sure if using it correctly though)
<%= simple_form_for [#studio, #course] do |f| %>
<%= f.input :name %>
<%= f.input :description %>
<% #categories.each do |category| %>
<%= check_box_tag "course[category_ids][]", category.id, true %>
<%= category.name%>
<% end %>
<%= f.button :submit %>
<% end %>
And all is permitted and created on the courses controller:
def new
#studio = Studio.find(params[:studio_id])
#course = Course.new
#course.studio = #studio
#categories = Category.all
end
def create
#studio = Studio.find(params[:studio_id])
#course = Course.new(course_params)
#course.studio = #studio
#categories = params[:category_ids]
if #course.save
redirect_to course_path(#course)
else
render :new
end
end
def course_params
params.require(:course).permit(:studio_id, :name, :description, :category_ids)
end
With better_errors i know the categories are being requested, here the request info:
"course"=>{"name"=>"Course test", "description"=>"testing", "category_ids"=>["2", "3"]}, "commit"=>"Create Course", "controller"=>"courses", "action"=>"create", "studio_id"=>"16"}
but the categories are not saved on course_params, HasCategory instance or on the Course, i´ve tried with #course.categories = params[:category_ids] and other solutions without success.
How do i save the categories to the courses?
Try the following
Change the strong parameter with category_ids: []
def course_params
params.require(:course).permit(:studio_id, :name, :description, category_ids: [])
end
Comment out this line #categories = params[:category_ids]
Hope it helps

Rails conditional_select for nested attributes

I'm trying to setup the following: A User has many Groups through Memberships, a Group has many Events, and an Event has many Posts.
On my view to show a group with all of its events, I want a user to be able to write a new post by selecting the correct group from a drop down, writing a comment and submit. I'm currently using a collection_select to create the post, but the event_id is not getting passed to ActiveRecord, i.e. posts are created, but they do not have event_ids (or even comments):
class User < ActiveRecord::Base
has_many :memberships
has_many :groups, through: :memberships
has_many :posts
end
class Membership < ActiveRecord::Base
belongs_to :group
belongs_to :user
end
class Group < ActiveRecord::Base
has_many :memberships
has_many :events, dependent: :destroy
has_many :users, through: :memberships
end
class Event < ActiveRecord::Base
belongs_to :group
has_many :posts
end
class Post < ActiveRecord::Base
belongs_to :event
belongs_to :user
end
class GroupsController < ApplicationController
def show
#define new post
#new_post = Post.new
end
end
class PostsController < ApplicationController
def create
if #post = Post.create(params[post_params])
flash[:success] = "Post Created!"
else
redirect_to group_url
end
end
private
def post_params
params.require(:post).permit(:event_id, :comment)
end
end
<h1>New Post:</h1>
<%=form_for([#new_post]) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class = "field">
<%= f.label :event_name %>
<%= f.collection_select(:event_id, Event.all, :id, :title) %>
</div>
<div class = "field">
<%= f.text_area :comment, placeholder: "New Post..." %>
</div>
<%=f.submit "Submit", class: "btn btn-large btn-primary" %>
<%end%>
I have a feeling that because the routes are nested, group_id never is passed to the Posts controller, and so can never be set. But I'm sure there's a lot more wrong than that...
can you try to pass Post.create(post_params) instead of Post.create(params[post_params])
post_params is actually a full hash extracted from the params so you should not pass it to params again
If you want to add user_id
you should add to your view something like this
<%= f.hidden_field :user_id, value: current_user.id %>

rails nested form with has_many :through relationship

Here are my models:
class Project < ActiveRecord::Base
has_many :project_applications
has_many :questions
accepts_nested_attributes_for :questions, :allow_destroy => true, :reject_if => proc { |a| a[:content].blank? }
end
class Question < ActiveRecord::Base
belongs_to :project
has_many :answers
has_many :project_applications, through: :answers
end
class Answer < ActiveRecord::Base
belongs_to :question
belongs_to :project_application
end
class ProjectApplication < ActiveRecord::Base
belongs_to :project
belongs_to :student
has_many :answers
has_many :questions, through: :answers
end
A project is created by an employer, and a student can create a project_application. The project_application should present the questions and then show form fields that correspond to the questions answers. I cannot for the life of me figure out how the form view should look. I need a form_for ProjectApplication that accepts nested attributes for answers. I have the following in my controller:
class ProjectApplicationsController < ApplicationController
def new
#project = Project.find(params[:project_id])
#project_application = ProjectApplication.new
#project_application.project = #project
#project_application.project.questions.each do |question|
#answer = question.answers.build
#answer.project_application = #project_application #this line does not work
puts 'answer' + #answer.inspect.to_s
end
puts 'here are the answers' + #project_application.answers.inspect.to_s
end
end
The problem with this is that the answers are not correctly being associated with project_applications because the project_applications don't have an id yet (because they have not been created) so the association can't happen, so the answer fields are not displayed. Here is the view code (does not work) that I have now:
<%= form_for #project_application, url: project_project_applications_path(#project.id), method: :post, remote: true do |f| %>
<%= f.fields_for :project do |proj| %>
<%= proj.fields_for :questions do |quest| %>
<%= quest.fields_for :answers do |answer| %>
<%= answer.text_area :content %>
<% end %>
<% end %>
<% end %>
<%= f.submit "APPLY" %>
<% end %>
How do I change the view and/or controller to properly display answer fields correctly associated with questions and the project application?
My understanding:
You have projects
Each project has many questions & project_applications
Each question belongs to a project, has many answers through project_applications
For each project, you'd like to create applications with the applicable answers. Here's what I'd do:
Models
class Project < ActiveRecord::Base
has_many :project_applications
has_many :questions
end
class Question < ActiveRecord::Base
belongs_to :project
has_many :answers
has_many :project_applications, through: :answers
end
class Answer < ActiveRecord::Base
belongs_to :question
belongs_to :project_application
end
class ProjectApplication < ActiveRecord::Base
belongs_to :project
belongs_to :student
has_many :answers
has_many :questions, through: :project
accepts_nested_attributes_for :answers
def self.build
application = self.new
application.answers.build
end
end
Controller
#app/controllers/project_applications_controller.rb
def new
#projectapplication = ProjectApplication.build
#project = #projectapplication.project
end
def create
end
private
def application_params
params.require(:project_application).permit(:application, :attributes, :here, answer_attributes: [:content])
end
Views
#app/views/project_applications/new.html.erb
<%= form_for #projectapplication do |f| %>
<%= f.text_field :application_fields %>
<% #project.questions.each do |question| %>
<%= f.fields_for :answers, question do |answer| %>
<%= answer.hidden_field :question_id, question.id %>
<%= answer.text_field :content, placeholder: question.content %>
<% end %>
<% end %>
<% end %>
Process
This works by creating a new project_application, sending answers directly to the answer model. Because answers are directly associated with questions, and projectsthrough questions, you should be able to get it working without passing any more data
This might not work outright, but I'm sure with some tweaking it will deliver the desired result

Resources