I'm trying to create a nested form in Ruby on Rails. The form appears as expected. But when it is saved the nested attribute, Booking, is not saved.
cleaner.rb
class Cleaner < ActiveRecord::Base
validates_presence_of :first_name
validates_presence_of :last_name
validates_presence_of :quality_score
validates :quality_score, inclusion: 0.0...5.0
has_many :assignments
has_many :bookings
has_many :cities, through: :assignments
has_many :customers, through: :bookings
end
customer.rb
class Customer < ActiveRecord::Base
validates_presence_of :first_name
validates_presence_of :last_name
validates_uniqueness_of :phone_number
belongs_to :city
has_many :bookings
has_many :cleaners, through: :bookings
accepts_nested_attributes_for :bookings
end
booking.rb
class Booking < ActiveRecord::Base
belongs_to :customer
belongs_to :cleaner
validates_presence_of :customer
validates_presence_of :cleaner
validates_presence_of :date
end
customers_controller.rb
class CustomersController < ApplicationController
before_action :set_customer, only: %i[show edit update destroy]
def index
#customers = Customer.all
end
def show; end
def new
#customer = Customer.new
end
def edit; end
def create
#customer = Customer.find_or_initialize_by(phone_number: params[:phone_number])
#customer.assign_attributes(customer_params)
respond_to do |format|
if #customer.save
format.html { redirect_to #customer, notice: 'Customer was successfully created.' }
format.json { render :show, status: :created, location: #customer }
else
format.html { render :new }
format.json { render json: #customer.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #customer.update(customer_params)
format.html { redirect_to #customer, notice: 'Customer was successfully updated.' }
format.json { render :show, status: :ok, location: #customer }
else
format.html { render :edit }
format.json { render json: #customer.errors, status: :unprocessable_entity }
end
end
end
def destroy
#customer.destroy
respond_to do |format|
format.html { redirect_to customers_url, notice: 'Customer was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_customer
#customer = Customer.find(params[:id])
end
def customer_params
params.require(:customer).permit(:first_name, :last_name, :phone_number, :city, :city_id, bookings_attributes: %i[cleaner_id date])
end
end
HomeController.rb
class HomeController < ApplicationController
def index
#customer = Customer.new
#customer.bookings.build
end
end
Parameters
{"utf8"=>"✓", "authenticity_token"=>"c4xo2M4r57+/xBsmcc+7yajpQU13u1kiwmOthx/nP7HiJXJIfS9/OqC0MrWCcaDrSW/xN8UGk2+LVfnUnbTb3A==", "customer"=>{"first_name"=>"adfad", "last_name"=>"fad", "phone_number"=>"9392323", "city_id"=>"1", "bookings_attributes"=>{"0"=>{"cleaner_id"=>"1"}}}, "#<ActionView::Helpers::FormBuilder:0x007f86bec96480>"=>{"date(1i)"=>"2017", "date(2i)"=>"8", "date(3i)"=>"2"}, "commit"=>"Create Customer"}
updated form
<h1>Sign Up Now</h1>
<%= form_for #customer do |f| %>
<% if #customer.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#customer.errors.count, "error") %> prohibited this customer from being saved:</h2>
<ul>
<% #customer.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :first_name %><br>
<%= f.text_field :first_name %>
</div>
<div class="field">
<%= f.label :last_name %><br>
<%= f.text_field :last_name %>
</div>
<div class="field">
<%= f.label :phone_number %><br>
<%= f.text_field :phone_number %>
</div>
<div class="field">
<%= f.label :city %><br>
<%= f.select :city_id, options_for_select(City.pluck(:name, :id)) %>
</div>
<%= f.fields_for :bookings do |b| %>
<%= b.date_select :date %>
<br>
<%= b.select :cleaner_id, Cleaner.all.pluck(:first_name, :id) %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
It's now failing with 2 errors
Bookings customer can't be blank
Bookings date can't be blank
use the iteration of the first #customer so rails can read properly
i saw this example https://www.sitepoint.com/complex-rails-forms-with-nested-attributes/
<%= f.fields_for :booking do |ff| %>
<%= ff.select :city_id,options_for_select(City.pluck(:name, :id)) %>
i hope it works cross finger :)
Related
I am building simple ROR app which has survey question and answers. Survey is generated using scaffolding method while question and answer are model only.
Survey.rb
class Survey < ApplicationRecord
has_many :questions
accepts_nested_attributes_for :questions, allow_destroy: true
validates :name, presence: true
end
Question.rb
class Question < ApplicationRecord
belongs_to :survey
has_many :answers
accepts_nested_attributes_for :answers
validates :question_content, presence: true
end
Answer.rb
class Answer < ApplicationRecord
belongs_to :question
end
survey_controller.rb
class SurveysController < ApplicationController
before_action :set_survey, only: [:show, :edit, :update, :destroy]
def index
#surveys = Survey.all
end
def show
#survey= Survey.find(params[:id])
end
def new
#survey = Survey.new
#questions = #survey.questions.new
#answers = #questions.answers.new
end
def edit
end
def create
#survey = Survey.new(survey_params)
Survey.create(survey_params)
respond_to do |format|
if #survey.save
format.html { redirect_to #survey, notice: 'Survey was successfully created.' }
format.json { render :show, status: :created, location: #survey }
else
format.html { render :new }
format.json { render json: #survey.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #survey.update(survey_params)
format.html { redirect_to #survey, notice: 'Survey was successfully updated.' }
format.json { render :show, status: :ok, location: #survey }
else
format.html { render :edit }
format.json { render json: #survey.errors, status: :unprocessable_entity }
end
end
end
def destroy
#survey.destroy
respond_to do |format|
format.html { redirect_to surveys_url, notice: 'Survey was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_survey
#survey = Survey.find(params[:id])
end
def survey_params
params.require(:survey).permit(:name, questions_attributes: [:id, :question_content], answers_attributes: [:id, :answer_content, :answer_type])
end
end
Survey form partial
<%= form_for(#survey) do |form| %>
<% if survey.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(survey.errors.count, "error") %> prohibited this survey from being saved:</h2>
<ul>
<% survey.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= form.label :name %>
<%= form.text_field :name, id: :survey_name %>
</div>
<%= form.fields_for :questions do |builder| %>
<fieldset>
<%= builder.label :question_content, "Question" %><br/>
<%= builder.text_area :question_content %><br/>
<%= form.fields_for :answers do |f| %>
<fieldset>
<%= f.text_area :answer_type %>
<%= f.text_area :answer_content %><br/>
</fieldset>
</fieldset>
<% end %>
<%end %>
<div class="actions">
<%= form.submit %>
</div>
I want to implement question and answer in single page i.e survey. Any help will appreciated.
Problem : Unable to save answers_attributes to database but Question attributes work perfectly fine. I am probably making mistake somewhere in controller not sure.
You need to change the way you're building the answer fields, so that they're referenced as fields_for questions, not surveys. Try changing this piece of code:
<%= form.fields_for :questions do |builder| %>
<fieldset>
<%= builder.label :question_content, "Question" %><br/>
<%= builder.text_area :question_content %><br/>
<%= form.fields_for :answers do |f| %>
<fieldset>
<%= f.text_area :answer_type %>
<%= f.text_area :answer_content %><br/>
</fieldset>
</fieldset>
<% end %>
<%end %>
to this:
<%= form.fields_for :questions do |builder| %>
<fieldset>
<%= builder.label :question_content, "Question" %><br/>
<%= builder.text_area :question_content %><br/>
<%= builder.fields_for :answers do |f| %>
<fieldset>
<%= f.text_area :answer_type %>
<%= f.text_area :answer_content %><br/>
</fieldset>
<% end %>
</fieldset>
<% end %>
(Note: the meaningful change is changing form.fields_for to builder.fields_for)
I'm very new to Rails development and having a problem saving multiple images/attachments to a model. My problem is that the code below is not actually saving to the item_images table when I submit the form. I am following This Article as a guide, though it seems to be a bit out of date. I feel I'm in a little over my head at this point so I hope someone can point out what I'm missing. Thanks!
I have the following models:
item.rb
class Item < ActiveRecord::Base
has_many :item_images, :dependent => :destroy
accepts_nested_attributes_for :item_images, :reject_if => lambda { |t| t['item_image'].nil? }
end
item_image.rb
class ItemImage < ActiveRecord::Base
belongs_to :item
has_attached_file :image,
:styles => { thumb: "100x100#", small: "400x400#", large: "700x700" }
validates_attachment_content_type :image, :content_type => /\Aimage\/.*\Z/
end
My controller looks like this:
items_controller.rb
class ItemsController < ApplicationController
before_action :set_item, only: [:show, :edit, :update, :destroy]
# GET /items
# GET /items.json
def index
#items = Item.all
end
# GET /items/1
# GET /items/1.json
def show
end
# GET /items/new
def new
#item = Item.new
4.times {#item.item_images.build}
end
# GET /items/1/edit
def edit
4.times {#item.item_images.build}
end
# POST /items
# POST /items.json
def create
#item = Item.new(item_params)
respond_to do |format|
if #item.save
format.html { redirect_to #item, notice: 'Item was successfully created.' }
format.json { render :show, status: :created, location: #item }
else
format.html { render :new }
format.json { render json: #item.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /items/1
# PATCH/PUT /items/1.json
def update
respond_to do |format|
if #item.update(item_params)
format.html { redirect_to #item, notice: 'Item was successfully updated.' }
format.json { render :show, status: :ok, location: #item }
else
format.html { render :edit }
format.json { render json: #item.errors, status: :unprocessable_entity }
end
end
end
# DELETE /items/1
# DELETE /items/1.json
def destroy
#item.destroy
respond_to do |format|
format.html { redirect_to items_url, notice: 'Item was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_item
#item = Item.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def item_params
params.require(:item).permit(:title, :description, :price, :available, :sort_shop, :sort_gallery, :item_type, :size)
end
end
form.html.erb
<%= form_for #item, html: { multipart: true } do |f| %>
<% if #item.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#item.errors.count, "error") %> prohibited this item from being saved:</h2>
<ul>
<% #item.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div class="field">
<%= f.label :price %><br>
<%= f.text_field :price %>
</div>
<div class="field">
<%= f.label :available %><br>
<%= f.check_box :available %>
</div>
<div class="field">
<%= f.label :sort_shop %><br>
<%= f.number_field :sort_shop %>
</div>
<div class="field">
<%= f.label :sort_gallery %><br>
<%= f.number_field :sort_gallery %>
</div>
<div class="field">
<%= f.label :item_type %><br>
<%= f.text_field :item_type %>
</div>
<div class="field">
<%= f.label :size %><br>
<%= f.text_field :size %>
</div>
<%= f.fields_for :item_images do |builder| %>
<% if builder.object.new_record? %>
<div class="field">
<%= builder.label :image, "Image File" %>
<%= builder.file_field :image %>
</div>
<% end %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Try this in strong parameters in Items controller
params.require(:item).permit(:title, :description, :price, :available, :sort_shop, :sort_gallery, :item_type, :size,item_images_attributes: [:image ])
than in ItemImage.rb add this line
belongs_to :item, optional: true,
and remove this line from Item.rb
:reject_if => lambda { |t| t['item_image'].nil? }
`
If you get any error please reply
I am trying to build a form for a has_many :through relationship. The Problem is, that I can access the attributes from the join table (:through) in the form, but not the other table.
My models look like this:
class Recipe < ActiveRecord::Base
has_many :quantities
has_many :ingredients, through: :quantities
accepts_nested_attributes_for :quantities
end
class Quantity < ActiveRecord::Base
belongs_to :recipe
belongs_to :ingredient
end
class Ingredient < ActiveRecord::Base
has_many :quantities
has_many :recipes, through: :quantities
end
My Form:
<%= form_for(#recipe) do |f| %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div class="quantities">
<%= f.fields_for :quantities do |builder| %>
<%= render 'quantity_fields', :f => builder%>
<% end %>
<%= link_to_add_association 'Add', f, :quantities %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
And the quantity_fields partial:
<p>
<%= f.label :value, "Value" %>
<%= f.text_field :value %>
<%= f.label :unit, "Unit" %>
<%= f.text_field :unit %>
<%= f.text_field :ingredient %>
<%= f.fields_for :ingredient do |builder|%>
<%= builder.label :name, "Ingredient"%>
<%= builder.text_field :name %>
<% end %>
</p>
The fields for the quantities attributes are correct, but the text field for the ingredient name (within the quantity_fields partial) stays empty.
On the Rails console on the other hand I can easily use the ingredient method on a quantity object and get a result.
I try to find a solution to this since a while now, and it becomes really frustrating to me. I am sure this is a standard task and I am just missing a small part. Can anyone help me?
Edit:
Also when I change my recipe form to directly render the ingredients, it doesn't work. The text fields remain empty.
<%= form_for(#recipe) do |f| %>
<div class="field">
<%= f.label :title %><br>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div class="ingredients">
<%= f.fields_for :ingredients do |builder| %>
<p>
<%= builder.label :name, "Name" %>
<%= builder.text_field :name %>
<%= builder.label :description, "Description" %>
<%= builder.text_field :description %>
</p>
<% end %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Edit 2:
The recipes controller:
class RecipesController < ApplicationController
before_action :set_recipe, only: [:show, :edit, :update, :destroy]
# GET /recipes
# GET /recipes.json
def index
#recipes = Recipe.all
end
# GET /recipes/1
# GET /recipes/1.json
def show
end
# GET /recipes/new
def new
#recipe = Recipe.new
end
# GET /recipes/1/edit
def edit
end
# POST /recipes
# POST /recipes.json
def create
#recipe = Recipe.new(recipe_params)
respond_to do |format|
if #recipe.save
format.html { redirect_to #recipe, notice: 'Recipe was successfully created.' }
format.json { render :show, status: :created, location: #recipe }
else
format.html { render :new }
format.json { render json: #recipe.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /recipes/1
# PATCH/PUT /recipes/1.json
def update
respond_to do |format|
if #recipe.update(recipe_params)
format.html { redirect_to #recipe, notice: 'Recipe was successfully updated.' }
format.json { render :show, status: :ok, location: #recipe }
else
format.html { render :edit }
format.json { render json: #recipe.errors, status: :unprocessable_entity }
end
end
end
# DELETE /recipes/1
# DELETE /recipes/1.json
def destroy
#recipe.destroy
respond_to do |format|
format.html { redirect_to recipes_url, notice: 'Recipe was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_recipe
#recipe = Recipe.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def recipe_params
params.require(:recipe).permit(:title, :description, quantities_attributes: [:value, :unit, :recipe, :ingredient, :_destroy])
end
end
When utilizing a has_many :through association in that way, you can set up your models like this:
class Recipe < ActiveRecord::Base
has_many :quantities
has_many :ingredients, inverse_of: :recipes
accepts_nested_attributes_for :quantities
end
class Quantity < ActiveRecord::Base
belongs_to :recipe
belongs_to :ingredient
accepts_nested_attributes_for :ingredients
end
class Ingredient < ActiveRecord::Base
has_many :quantities
has_many :recipes, inverse_of: :ingredients
end
This article covers nested attributes with has_many :through associations in a similar context. Hope this helps!
I am working on a website, where a user can have multiple projects and multpile users can contribute to a single project.
I have a project model
class Project < ActiveRecord::Base
#associations
has_and_belongs_to_many :users
end
and a users model
class User < ActiveRecord::Base
#associations
has_and_belongs_to_many :projects
end
and I have created a joins table by the name - :projects_users
everything works fine when I run the code on rails console.
but when I try to do the save thing in the controller, the data is not being saved in the joins table.
Code for the controller
please help
class ProjectsController < ApplicationController
def new
#project = Project.new
end
def create
#user = User.find(session[:user_id])
#project = Project.new(project_params)
if #project.save
#project.users << #user
redirect_to #project
else
flash[:error] = "Project has not been created due to some error"
render 'new'
end
end
private
def project_params
params.require(:project).permit(:name,:description)
end
end
Try using nestes_attributes_for
class Answer < ActiveRecord::Base
belongs_to :question
end
class Question < ActiveRecord::Base
has_many :answers
accepts_nested_attributes_for :answers, allow_destroy: true
end
Controlller
def new
#question = Question.new
#question.answers.build
end
def create
#question = Question.new(question_params)
respond_to do |format|
if #question.save
format.html { redirect_to #question, notice: 'question was successfully created.' }
format.json { render action: 'show', status: :created, location: #question }
else
format.html { render action: 'new' }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
def question_params
params.require(:question).permit(:name, :description, answers_attributes:[:content, :id, :question_id])
end
You form should look like this
<%= form_for(#question) do |f| %>
<% if #question.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#question.errors.count, "error") %> prohibited this question from being saved:</h2>
<ul>
<% #question.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_field :description %>
</div>
<%= f.fields_for :answer do |builder| %>
<%= builder.label :content %>
<%= builder.text_area :content %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
just changed
#project.users << #user
to
#user.projects << #project
and it started working.Dont know the reason yet but its working
I am having a hard time with what I thought would be an textbook update example. Searched SO but couldn't find the answer. In short, when I click submit the user_id in the profile model gets wiped out and no other data gets saved. I am using Rails 3.2.2.
Here is what I have...
User model...
class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation, :profile_attributes
has_one :profile
accepts_nested_attributes_for :profile
end
Profile model...
class Profile < ActiveRecord::Base
validates :first_name, :presence => true
validates :last_name, :presence => true
belongs_to :user
attr_accessible :first_name, :last_name
end
Users controller...
class UsersController < ApplicationController
def new
#user = User.new
#user.accounts_users.build()
#user.build_profile()
respond_to do |format|
format.html # new.html.erb
format.json { render json: #user }
end
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to #user, notice: 'Profile was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
end
Nested form...
<%= form_for #user, :validate => true do |f| %>
<%= f.fields_for :profile do |p| %>
<fieldset>
<div class="field">
<%= p.label :first_name %>
<%= p.text_field :first_name %>
</div>
<div class="field">
<%= p.label :last_name %>
<%= p.text_field :last_name %>
</div>
<% end %>
<div class="field">
<%= f.label :email %>
<%= f.text_field :email %>
</div>
<div class="actions">
<%= f.submit 'Edit Profile', :class => "btn btn-large btn-success" %>
<%= cancel %>
</div>
</fieldset>
<% end %>
Edit: I edited the UsersController to include the new action. Why would the new action affect the edit/update actions?
I had a similar problem before. Can we see the code for your new action? Do you have #user.build_profile in there (right beneath #user = User.new)? Or is the new action working fine?