How to handle multiple models in one rails form? - ruby-on-rails

I have the following models
class Survey < ActiveRecord::Base
has_many :survey_sections
accepts_nested_attributes_for :survey_sections
end
class SurveySection < ActiveRecord::Base
belongs_to :survey
has_many :questions
accepts_nested_attributes_for :questions
end
class Question < ActiveRecord::Base
belongs_to :survey_section
has_many :answers
belongs_to :question_group
accepts_nested_attributes_for :question_group
accepts_nested_attributes_for :answers
end
class Answer < ActiveRecord::Base
belongs_to :question
end
class QuestionGroup < ActiveRecord::Base
has_many :questions
end
My Controller:
def new
#survey = Survey.new
survey_section = #survey.survey_sections.build
survey_section.questions.build
end
def create
#survey = Survey.new(survey_params)
if #survey.save
redirect_to #survey, notice: 'Super'
else
render 'new'
end
end
def survey_params
params.require(:survey).permit(:title, :description, survey_sections_attributes:[:id, :title, questions_attributes:[:id, :text, answers_attributes:[:id, :text]]])
end
How it is possible to save data in more then 3 models?
At the moment i can save from my survey form data into the survey, survey section and question model. But i don't know what i have to in the controller that i can save data into the other models.

You can handle as many forms as you need, if you use the fields_for helper properly.
This is where you're falling short I think (your controller seems okay).
I also wrote an answer about this some time back.
#app/models/survey.rb
class Survey < ActiveRecord::Base
has_many :sections
accepts_nested_attributes_for :sections
end
#app/models/section.rb
class Section < ActiveRecord::Base
belongs_to :survey
has_many :questions
accepts_nested_attributes_for :questions
end
#app/models/question.rb
class Question < ActiveRecord::Base
belongs_to :section
has_many :answers
end
Try and keep your model names as succinct as possible.
#app/controllers/surveys_controller.rb
class SurveysController < ApplicationController
def new
#survey = Survey.new
#survey.sections.build.questions.build
end
def create
#survey = Survey.new survey_params
#survey.save
end
private
def survey_params
params.require(:survey).permit(:title, sections_attributes: [:title, questions_attributes:[:title]])
end
end
#app/views/surveys/new.html.erb
<%= form_for #survey do |f| %>
<%= f.text_field :title %>
<%= f.fields_for :sections do |section| %>
<%= section.text_field :title %>
<%= section.fields_for :questions do |question| %>
<%= question.text_field :title %>
<% end %>
<% end %>
<%= f.submit %>
<% end %>

You can get best explanation here with same type of model
http://railscasts.com/episodes/196-nested-model-form-part-1
#app/models/survey.rb
class Survey < ActiveRecord::Base
has_many :sections, :dependent => :destroy
accepts_nested_attributes_for :sections, :allow_destroy => true
end
#app/models/section.rb
class Section < ActiveRecord::Base
belongs_to :survey
has_many :questions, :dependent => :destroy
accepts_nested_attributes_for :questions, :allow_destroy => true
end
#app/models/question.rb
class Question < ActiveRecord::Base
belongs_to :section
has_many :answers
end
now in controller
def new
#survey = Survey.new
section = #survey.sections.build
section.questions.build
end
end
Now in views
<%= form_for #survey do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<%= f.fields_for :sections do |builder| %>
<%= builder.text_field :title %>
<%= builder.fields_for :questions do |question| %>
<%= question.text_field :content%>
<% end %>
<% end %>
<p><%= f.submit "Submit" %></p>
<% end %>

Related

accept nested attributes for has_many & has_many through in same form_for

class User < ActiveRecord::Base
has_many :tasks
has_many :task_items, through: :task
accepts_nested_attributes_for :task
accepts_nested_attributes_for :task_item
end
class Task < ActiveRecord::Base
belongs_to :user
has_many :task_item
end
class TaskItem < ActiveRecord::Base
belongs_to :task
end
I am able to get & save form data for Task using form_for in the user form.
<%= fields_for :user, user do |f| -%>
<%= f.fields_for :task do |builder| %>
<%=builder.text_field :name%>
<%end%>
<% end %>
I want to accept attributes for Task as well as TaskItem in the User form itself using form_for.
Unable to figure out how to do this.
I tried with:
<%= fields_for :user, user do |f| -%>
<%= f.fields_for :task do |builder| %>
<%=builder.text_field :name%>
<%= f.fields_for builder.object.checklist do |builder_1| %>
<%builder_1.object.each do |bb|%>
<%= bb.check_box :completed%>
<%end%>
<%end%>
<%end%>
It gives undefined method `check_box' for #
I want to be able to create a User,its task & task item records all using one form.
Any solutions are welcome.
You have many pluralization errors. Check these changes:
class User < ActiveRecord::Base
has_many :tasks
has_many :task_items, through: :tasks #not task
accepts_nested_attributes_for :tasks #not task
accepts_nested_attributes_for :task_items #not task_item
end
class Task < ActiveRecord::Base
belongs_to :user
has_many :task_items #not task_item
end
class TaskItem < ActiveRecord::Base
belongs_to :task
end
Then, the view:
<%= fields_for :user, user do |f| %>
<%= f.fields_for :tasks do |builder| %>
<%= builder.text_field :name %>
<%= builder.fields_for :task_items do |ti| %>
<%= ti.check_box :completed %>
<% end %>
<% end %>
<% end %>

Create controller method with accepts_nested_attributes_for and fields_for

i'm building a web application with Rails 5.2.0 about recipes and I have a doubt about the create method of the controller.
This are my models:
class Recipe < ApplicationRecord
belongs_to :user
has_many :quantities
has_many :ingredients, through: :quantities
accepts_nested_attributes_for :quantities, allow_destroy: true
end
class Quantity < ApplicationRecord
belongs_to :recipe
belongs_to :ingredient
end
class Ingredient < ApplicationRecord
has_many :quantities
has_many :recipes, through: :quantities
end
And here the view to create new recipes:
<%= form_for(#recipe) do |f| %>
<%= f.label :name, "Name" %>
<%= f.text_field :name %>
<%= f.label :servings, "Servings" %>
<%= f.number_field :servings %>
<%= f.fields_for :quantities do |quantity| %>
<%= f.hidden_field :_destroy, class: "hidden-field-to-destroy" %>
<%= f.label :ingredient_id, "Ingredient Name" %>
<%= f.text_field :ingredient_id%>
<%= f.label :amount, "Amount" %>
<%= f.number_field :amount %>
<%= f.label :unit, "Unit" %>
<%= f.select(:unit, ["kg","g","l","ml"], {include_blank: true}) %>
<% end %>
<%= f.submit 'Add new recipe' %>
<% end %>
I can add new ingredients dynamically with jquery and also delete them in the same form.
The update method of the controller works perfectly but the create method does not work:
class RecipesController < ApplicationController
def create
#recipe = current_user.recipes.build(recipe_params)
if #recipe.save
flash[:success] = "New recipe created correctly."
redirect_to #recipe
else
render 'new'
end
end
def update
#recipe = Recipe.find(params[:id])
if #recipe.update_attributes(recipe_params)
flash[:success] = "The recipe has been updated correctly."
redirect_to #recipe
else
render 'edit'
end
end
private
def recipe_params
params.require(:recipe).permit( :name, :servings, quantities_attributes: [:ingredient_id, :amount, :unit,:_destroy, :id, :recipe_id])
end
end
I'm trying to do #recipe = current_user.recipes.build(recipe_params) but I get the following error in te view:
Quantities recipe can't be blank
I think this occurs because when trying to create the relation, it is necessary to indicate the recipe_id, but the recipe has not yet been created and the id can not be indicated.
Could you please tell me someone what would be the correct way to create the recipe first and then be able to add the ingredients through Quantity in the create method of the recipe controller?
As per the message shared the qunatity_recipes cannot be blank and you haven't specified any condition to manage this.
Current
class Recipe < ApplicationRecord
belongs_to :user
has_many :quantities
has_many :ingredients, through: :quantities
accepts_nested_attributes_for :quantities, allow_destroy: true
end
Update the accepts nested attributes to allow_nil for Recipe class
class Recipe < ApplicationRecord
belongs_to :user
has_many :quantities
has_many :ingredients, through: :quantities
accepts_nested_attributes_for :quantities, allow_destroy: true, allow_nil: true
end

using has_many through with dynamic forms rails 4

I want to create an appointment form with dynamics forms. But I'm not getting what I'm doing wrong
I'm having trouble with the partial _appointment_record_form_fields and with appointment_controller.
Someone can help me fix the controller and the view?
My Models:
class Appointment < ActiveRecord::Base
has_many :appointment_record_forms, dependent: :destroy
has_many :record_forms, through: :appointment_record_forms
accepts_nested_attributes_for :appointment_record_forms, allow_destroy: true
end
class AppointmentRecordForm < ActiveRecord::Base
belongs_to :appointment
belongs_to :record_form
serialize :properties, Hash
def validate_properties
record_form.record_form_fields.each do |record_form_field|
if record_form_field.required? && properties[record_form_field.name].blank?
errors.add record_form_field.name, "must not be blank"
end
end
end
end
class RecordForm < ActiveRecord::Base
has_many :record_form_fields
has_many :appointment_record_forms, dependent: :destroy
has_many :appointments, through: :appointment_record_forms
accepts_nested_attributes_for :record_form_fields, allow_destroy: true
end
class RecordFormField < ActiveRecord::Base
belongs_to :record_form
end
My Controller
class AppointmentsController < ApplicationController
.
.
.
def appointment_params
params.require(:appointment).permit(:duration, :appointment_date, :patient_id, :doctor_id,
:record_forms => [:id, :name, :_destroy],
:record_form_fields => [:id, :name, :field_type, :require, :record_form_id, :_destroy])
end
end
Views:
views/appointment/_form
...
<%= f.fields_for :appointment_record_forms do |builder| %>
<%= render 'appointment_record_form_fields', f: builder %>
<% end %>
<%= link_to_add_fields "Add Field", f, :appointment_record_forms %>
...
partial/_appointment_record_form_fields
<fieldset>
<%= f.fields_for :properties, OpenStruct.new(#appointment_record_forms.properties) do |builder| %>
<% #appointment_record_forms.record_form.record_form_fields.each do |record_form_field| %>
<%= render "appointments/fields/#{record_form_field.field_type}", record_form_field: record_form_field, f: builder %>
<% end %>
<% end %>
<%= f.hidden_field :_destroy %>
<%= link_to "remove", '#', class: "remove_fields" %>
</fieldset>
TKS

Rails Strong Params & Nested Forms: "Unpermitted parameter: answer"

I have an Assessment model which has many questions and many answers through questions
class Assessment < ActiveRecord::Base
belongs_to :template
belongs_to :patient
has_many :questions, :through=> :template
has_many :answers, :through=> :questions
accepts_nested_attributes_for :answers
end
class Question < ActiveRecord::Base
belongs_to :template
has_many :answers
end
class Answer < ActiveRecord::Base
belongs_to :question
belongs_to :assessment
end
I want to create an assessment form which can accept nested attributes for answers.
Here's my assessment form:
<%= form_for #assessment do |f| %>
<div role="tabpanel" class="tab-pane active" id="subjective">
<% #assessment.questions.each do |question| %>
<%= render 'question_fields', :question=> question, :f=> f %>
<% end %>
</div>
<%= f.hidden_field :patient_id, :value=> #assessment.patient.id %>
<%= f.hidden_field :template_id, :value=> #assessment.id %>
<%= f.submit 'Save', :class=> "btn btn-primary", :style=>"width: 100%;" %>
<% end %>
And my question_fields:
<p><%= question.content %></p>
<%= f.fields_for :answer do |builder| %>
<div class="row">
<div class="col-sm-10 question">
<% if question.field_type == "text_area" %>
<%= builder.text_area :content, :class=>"form-control" %>
<% end %>
</div>
</div>
And finally my action:
def create
#assessment = current_user.assessments.new(assessment_params)
if #assessment.save
redirect_to assessments_path
else
render :new
end
end
protected
def assessment_params
params.require(:assessment).permit(
:template_id, :patient_id,
:answers_attributes => [:id, :question_id, :assessment_id, :tracking, :content])
end
When I submit my form, I get taken to the assessments_path, but my log shows "unpermitted params answer". I know somewhere I've gone wrong with my pluralization or associations, but not sure exactly where.
Give this a shot, it looks like you're accepts_nested_attributes_for :answers is in the wrong place and you're missing the one for questions.
class Assessment < ActiveRecord::Base
belongs_to :template
belongs_to :patient
has_many :questions, :through=> :template
has_many :answers, :through=> :questions
accepts_nested_attributes_for :questions
end
class Question < ActiveRecord::Base
belongs_to :template
has_many :answers
accepts_nested_attributes_for :answers
end
class Answer < ActiveRecord::Base
belongs_to :question
belongs_to :assessment
end

Rails 4: Being able to select multiple instances of one model when creating the model it has a has_many :through relationship with

When I add a new Book to my Library, I want to be able to specify what Shelves (note: "Shelf" is the term I used in my app for the model that groups Books together within a Library; a better name for this model would have been Category or Genre, but I was stupid) in that Library it should belong to. Right now, I'm able to add a Book to my Library, but I'm having issues with the Shelves. I'm pretty new at learning Rails and I'd appreciate any help.
Models
user.rb:
class User < ActiveRecord::Base
has_one :library, dependent: :destroy
...
end
library.rb:
class Library < ActiveRecord::Base
belongs_to :user
has_many :shelves, dependent: :destroy
has_many :catalogs, dependent: :destroy
has_many :books, :through => :catalogs, dependent: :destroy
...
end
book.rb:
class Book < ActiveRecord::Base
has_many :catalogs, dependent: :destroy
has_many :libraries, :through => :catalogs, dependent: :destroy
has_many :bookshelves, dependent: :destroy
has_many :shelves, :through => :bookshelves, dependent: :destroy
...
end
catalog.rb:
class Catalog < ActiveRecord::Base
belongs_to :book
belongs_to :library
end
shelf.rb:
class Shelf < ActiveRecord::Base
belongs_to :library
has_many :bookshelves, dependent: :destroy
has_many :books, :through => :bookshelves, dependent: :destroy
...
end
bookshelf.rb
class Bookshelf < ActiveRecord::Base
belongs_to :book
belongs_to :shelf
end
Controller
books_controller.rb
class BooksController < ApplicationController
...
def create
#book = Book.new(book_params)
#library = current_user.library
if #book.save
#book.catalogs.create(:library_id => #library.id)
flash[:success] = "Book added to library!"
redirect_to current_user
else
render 'current_user'
end
end
...
private
def book_params
params.require(:book).permit(:title, :author, :publisher, :isbn)
end
...
end
View
_book_form.html.erb:
<%= form_for(#book) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_field :title, placeholder: "Book Title" %>
<%= f.text_field :author, placeholder: "Author" %>
<%= f.text_field :publisher, placeholder: "Publisher" %>
<%= f.text_field :isbn, placeholder: "ISBN" %>
<%= f.fields_for :catalogs do |ff| %>
<%= ff.hidden_field :library_id %>
<% end %>
<%= f.fields_for :bookshelves do |ff| %>
<%= ff.collection_select :shelf_ids, current_user.library.shelves.all.collect {|x| [x.name, x.id]}, {}, multiple: true %>
<% end %>
</div>
<%= f.submit "Add Book to Library", class: "btn btn-primary" %>
<% end %>
In your book_params method, you don't have shelf_ids listed. So it won't be passed into the .new method and thus it won't be saved. Since you wan't multiple ids, you want to do this:
def book_params
params.require(:book).permit(:title, :author, :publisher, :isbn, shelf_ids: [])
end
That way, the controller knows it's an array and not just a single value.
Oh, and btw, shelf_ids and any other array values like that must be at the end or it will throw an error.
Edit:
Also change this part of code:
<%= f.fields_for :bookshelves do |ff| %>
<%= ff.collection_select :shelf_ids, current_user.library.shelves.all.collect {|x| [x.name, x.id]}, {}, multiple: true %>
<% end %>
to this:
<%= f.collection_select :shelf_ids, current_user.library.shelves.all.collect {|x| [x.name, x.id]}, {}, multiple: true %>
You don't need to worry about the through part of the association. Just set up the has_many part and it'll work.

Resources