Rails 4 nested attributes multiple records when updating - ruby-on-rails

I'm stuck and I don't know why it is not working right.
I have a model product which has many tags.
When I update the product rails update properly the products attributes but is creating another tag record instead of just updating it.
here is my code:
View form:
<%= form_for ([#product.user, #product]), id: 'edit_form' do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
<div class="field">
<%= f.fields_for :tags do |t| %>
<%= t.label :name %>
<%= t.text_field :name %>
<% end %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
product model:
class Product < ActiveRecord::Base
belongs_to :user, :foreign_key => "user_id"
has_many :tags, :dependent => :destroy
accepts_nested_attributes_for :tags, reject_if: :all_blank, allow_destroy: true, :update_only => true
end
tags model:
class Tag < ActiveRecord::Base
belongs_to :product, :foreign_key => "product_id"
# before_save { name.downcase! }
end
product controller:
def edit
user = User.find(params[:user_id])
#product = user.products.find(params[:id])
#tags = #product.tags.all
respond_to do |format|
format.html
format.js
end
end
def update
user = User.find(params[:user_id])
#product = user.products.find(params[:id])
#tags = #product.tags.all
respond_to do |format|
if #product.update(product_params)
format.html { redirect_to([#product.user, #product], :notice => 'Product successfully updated.') }
else
format.html { render :action => "edit" }
end
end
end
def product_params
params.require(:product).permit(:name, :description, tags_attributes: :name)
end

You have to pass the tag id in the permit params in your controller
def product_params
params.require(:product).permit(:name, :description, tags_attributes: [:id,:name])
end

Related

Rails - Convert hours and minutes to seconds then assign value to key

I would like to combine both values :hours and :minutes and convert them to to_i in seconds. Next is to assign this value (which should be in seconds) to the :time_duration which is a column in the cars db before it creates a new service. The :time_duration is in a hidden_field because there's no reason to render this data in the view.
views
This is my _car_fields.html.erb which is a nested partial inside a view template called, _form.html.erb .
_car_fields.html.erb
<div class="nested-fields">
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %><br>
<%= f.label :hours %>
<%= f.select :hours, '0'..'8' %>
<%= f.label :minutes %>
<%= f.select :minutes, options_for_select( (0..45).step(15), selected: f.object.minutes )%><br>
<%= f.label :price %><br>
<%= f.text_field :price, :value => (number_with_precision(f.object.price, :precision => 2) || 0) %> <br>
<%= f.label :details %><br>
<%= f.text_area :details %></div>
<%= link_to_remove_association "Remove Car", f, class: 'btn btn-default' %>
<%= f.hidden_field :time_duration, value: %>
<br>
<hr>
</div>
_form.html.erb
<%= simple_form_for #service do |f| %>
<div class="field">
<%= f.label "Select service category" %>
<br>
<%= collection_select(:service, :service_menu_id, ServiceMenu.all, :id, :name, {:prompt => true }) %>
<%= f.fields_for :cars do |task| %>
<%= render 'car_fields', :f => task %>
<% end %>
</div>
<div class="links">
<%= link_to_add_association 'Add New Car', f, :cars, class: 'btn btn-default' %>
</div><br>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
controller
services_controller
def new
#service = current_tech.services.build
end
def create
#service = current_tech.services.build(service_params)
respond_to do |format|
if #service.save
format.html { redirect_to #service, notice: 'Service was successfully created.' }
format.json { render :show, status: :created, location: #service }
else
format.html { render :new }
format.json { render json: #service.errors, status: :unprocessable_entity }
end
end
end
private
def service_params
params.require(:service).permit(:name, :service_menu_id, cars_attributes: [:tech_id, :name, :hours, :minutes, :price, :details, :_destroy])
end
models
service.rb
class Service < ActiveRecord::Base
belongs_to :tech
belongs_to :service_menu
has_many :cars, dependent: :destroy
accepts_nested_attributes_for :cars, :reject_if => :all_blank, :allow_destroy => true
end
car.rb
class Car< ActiveRecord::Base
belongs_to :service
belongs_to :tech
has_many :appointments
end
First, you can remove the hidden time_duration field from the form, since it is not needed.
Then, you'll create a before_save method for your car model:
car.rb
class Car < ActiveRecord::Base
belongs_to :service
belongs_to :tech
has_many :appointments
before_save :generate_time_duration
def generate_time_duration
self[:time_duration] = hours.hours.to_i + minutes.minutes.to_i
end
end
What this does: Before the car object is saved, it will run the generate_time_duration method. What this method does is it simply sums the hours.to_i and minutes.to_i and assigns it to the car's time_duration attribute.
Update old DB records
Since you're adding this functionality in your application AFTER records have already been created, here is a quick way to update all of your current records:
In your command line, open a rails console by running the command rails c (or rails console)
In the console, run this command: Car.all.each { |c| c.save! }
This is a quick, one-time fix that will loop through all Car records, save them, and subsequently update their time_duration fields.

Initialize associated object in rails

I have some trouble to fill my associated objects.
I have associated object, i try to fill my local DB's tables with form data, i can fill the building and all the object which composed itself : light_reseller, stage, furniture, location. But i don't find how to fill the component "points" of stage and
furniture !
here is my model building.rb
class Building < ActiveRecord::Base
has_many :stage, dependent: :destroy
has_many :furniture, dependent: :destroy
has_one :light_reseller, dependent: :destroy
has_one :location, dependent: :destroy
accepts_nested_attributes_for :light_reseller, :location, :stage, :furniture, :allow_destroy => true
end
class LightReseller < ActiveRecord::Base
belongs_to :building
end
class Location < ActiveRecord::Base
belongs_to :building
end
class Stage < ActiveRecord::Base
belongs_to :building
has_many :points, dependent: :destroy
accepts_nested_attributes_for :points
end
class Furniture < ActiveRecord::Base
belongs_to :building
has_many :points, dependent: :destroy
accepts_nested_attributes_for :points
end
class Point < ActiveRecord::Base
belongs_to :furniture
belongs_to :stage
end
I use a form to fill my objects :
<%= form_for (#building) do |f| %>
<% if #building.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#building.errors.count, "error") %> prohibited this store from being
saved:</h2>
<ul>
<% #building.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<h2>Store</h2>
<%= f.label :name %><br>
<%= f.text_field :name, class: "form-control" %>
<%= f.fields_for :location do |loc| %>
<%= loc.label :address %><br>
<%= loc.text_field :address, class: "form-control" %>
<%= loc.label :city %><br>
<%= loc.text_field :city, class: "form-control" %>
<% end %>
<%= f.fields_for :light_reseller do |lr| %>
<%= lr.label :"light_reseller name" %><br>
<%= lr.text_field :name, class: "form-control" %>
<% end %>
<h2>Stage</h2>
<%= f.fields_for :stage do |ft| %>
<%= ft.label :name %><br>
<%= ft.text_field :name, class: "form-control" %>
<%= ft.fields_for :point do |pt| %>
<%= pt.label :"point value" %><br>
<%= pt.text_field :val, class: "form-control" %>
<% end %>
<h3>Stage entries</h3>
<%= ft.fields_for :entrie do |et| %>
<%= et.label :"entrie name" %><br>
<%= et.text_field :name, class: "form-control" %>
<%= et.fields_for :point do |ept| %>
<%= ept.label :"point value" %><br>
<%= ept.text_field :val, class: "form-control" %>
<% end %>
<% end %>
<% end %>
<%= f.fields_for :furniture do |ft| %>
<h2>Furniture</h2>
<%= ft.label :name %><br>
<%= ft.text_field :name, class: "form-control" %>
<%= ft.fields_for :point do |pt| %>
<%= pt.label :"point value" %><br>
<%= pt.text_field :val, class: "form-control" %>
<% end %>
<%= ft.fields_for :point do |pt| %>
<%= pt.label :"point value" %><br>
<%= pt.text_field :val, class: "form-control" %>
<% end %>
<% end %>
</div>
<div class="actions">
<%= f.submit "Add a Store", class: "btn btn-default"%>
</div>
<% end %>
and here is my controller :
class BuildingsController < ApplicationController
before_action :set_building, only: [:show, :edit, :update, :destroy]
def index
#buildings = Building.all
end
def new
#building = Building.new
#building.build_location
#building.build_light_reseller
#building.furniture.new
#building.stage.build
end
def show
end
def create
#building = Building.new(buildings_params)
respond_to do |format|
if #building.save
format.html { redirect_to #building, notice: 'building was successfully created.' }
format.json { render :index, status: :created, location: #building }
else
format.html { render :new }
format.json { render json: #building.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #building.update(buildings_params)
format.html { redirect_to #building, notice: 'building was successfully updated.' }
format.json { render :show, status: :ok, location: #buildings }
else
format.html { render :edit }
format.json { render json: #buildings.errors, status: :unprocessable_entity }
end
end
end
def destroy
#building.destroy
respond_to do |format|
format.html { redirect_to buildings_path, notice: 'building was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_building
#building = Building.find(params[:id])
end
def buildings_params
params.require(:building).permit(:name, location_attributes:[:address, :city], light_reseller_attributes:[:name], furniture_attributes:[:name, point_attributes:[:val]], stage_attributes:[:name, point_attributes:[:val]])
end
So when i submit my form, my controller receive these data:
"building"=>{"name"=>"Fnac", "location_attributes"=>{"address"=>"12 bird road", "city"=>"Paris"}, "light_reseller_attributes"=>{"name"=>"Marc"}, "stage_attributes"=>{"0"=>{"name"=>"First", "point"=>{"val"=>"1235"}}}, "furniture_attributes"=>{"0"=>{"name"=>"shelf", "point"=>{"val"=>"45"}}}}
You have a relation has_many :points with furniture and stage, so you need to change
<%= ft.fields_for :point do |pt| %>
to
<%= ft.fields_for :points do |pt| %>
And also change point_attributes to points_attributes in your buildings_params.
Try the below changes too along with the above.
class Building < ActiveRecord::Base
has_many :stages, dependent: :destroy
has_many :furnitures, dependent: :destroy
has_one :light_reseller, dependent: :destroy
has_one :location, dependent: :destroy
accepts_nested_attributes_for :light_reseller, :location, :stages, :furnitures, :allow_destroy => true
end
def new
#building = Building.new
#building.build_location
#building.build_light_reseller
#building.furnitures.build
#building.stages.build
end
def buildings_params
params.require(:building).permit(:name, location_attributes:[:address, :city], light_reseller_attributes:[:name], furnitures_attributes:[:name, points_attributes:[:val]], stages_attributes:[:name, points_attributes:[:val]])
end
<%= f.fields_for :stage do |ft| %> to <%= f.fields_for :stages do |ft| %>
and
<%= f.fields_for :furniture do |ft| %> to <%= f.fields_for :furnitures do |ft| %>

Cannot access associated model attributes within a form

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 use many to many relationship but it not save data in through table

i have 2 model book and wishlist.
and between this 2 model i use many to many relationship.
Below is my model.
class Book < ActiveRecord::Base
has_many :book_wishlist_customizations, dependent: :destroy
has_many :wish_lists ,through: :book_wishlist_customizations
end
class BookWishlistCustomization < ActiveRecord::Base # through table
belongs_to :wish_list
belongs_to :book
end
class WishList < ActiveRecord::Base
has_many :book_wishlist_customizations
has_many :books,through: :book_wishlist_customizations
end
when i delete book from admin side it raise following error.
PG::ForeignKeyViolation: ERROR: update or delete on table "books" violates foreign key constraint "fk_rails_7a6b92667b" on table "wish_lists" DETAIL: Key (id)=(1) is still referenced from table "wish_lists". : DELETE FROM "books" WHERE "books"."id" = $1.
below is my book form.
<%= form_for #book, url: books_path do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.label :stream_id %>
<%= f.collection_select :stream_id, Stream.all, :id, :stream_name, prompt: true %>
</div>
</br>
<div class="field">
<%= f.label :university_board_id, "University" %>
<%= f.collection_select :university_board_id, UniversityBoard.where(category_id: $college_id).all, :id, :name, prompt: true %>
</div>
</br>
<div class="field">
<%= f.label :course_standard_id, "Course & Year" %>
<%=f.select(:course_standard_id, :"Please select" => true)%>
<%=f.select(:year_semester, :"Please select" => true)%>
</div>
</br>
<div class="field">
<%= f.label :college_school_id, "College" %>
<%=f.select(:college_school_id, :"Please select" => true)%>
</div>
</br>
<div class="field">
<%= f.label :subject_id, "Subject" %>
<%=f.select(:subject_id, :"Please select" => true)%>
</div>
</br>
<div class="field">
<%= f.label :book_name, "Book name" %>
<%= f.text_field :book_name %>
</div>
</br>
<div class="actions">
<%= f.submit "Continue", class: "btn btn-primary" %>
</div>
<% end %>
below is my controller:-
class BooksController < ApplicationController
def new
#book = Book.new
$college_id = Category.where(category_name: ['college']).select(:id)
$school_id = Category.where(category_name: ['school']).select(:id)
end
def create
if user_signed_in?
#book = Book.new(book_params)
#last_commision = Commision.last
#book_status = BookStatus.find_by(status_name: "pending")
#book.update_attributes( commision_id: #last_commision.id, book_status_id: #book_status.id, user_id: current_user.id, quantity: "1")
if #book.save
# exit
session[:book_id] = #book.id
# session[:photo_id] = #book_photo.id
redirect_to multi_steps_path
else
respond_to do |format|
format.html { render :new }
format.json { render json: #book.errors, status: :unprocessable_entity }
end
end
else
session[:book_details] = book_params
redirect_to unauthenticated_root_path, notice: "For post your book first login in our system."
end
end
def stream_change_course
#stream_details=CourseStandard.where(stream_id: params[:stream], category_id: $college_id)
render :json => #stream_details
end
def course_change_college_subject
#year_semester=CourseStandard.where(id: params[:course])
#subject_details=Subject.where(course_standard_id: CourseStandard.where(category_id: $college_id))
#college_details=CollegeSchool.where(course_standard_id: CourseStandard.where(category_id: $college_id))
render :json => {:subject => #subject_details,:college => #college_details,:year =>#year_semester}
end
def price_calculate_commission
#final_amount=params[:price].to_i-(params[:price].to_i*Commision.last.percentage)/100
render :json => #final_amount
end
private
def book_params
params.require(:book).permit(:book_id, :user_id, :book_name, :book_condition_id,:price_for_sale, :mrp, :book_auther, :isbn, :description, :publish_year, :edition, :publication, :book_status_id, :quantity, :category_id, :university_board_id, :college_school_id, :course_standard_id, :subject_id, :commision_id, :stream_id, :medium_id,:year_semester) rescue {}
# params.require(:book).permit(:stream_id, :university_board_id, :course_standard_id, :subject_id, :id)
end
def book_photo_params
params.require(:book_photo).permit(:photo) rescue {}
end
end
I'm guessing from your error that your WishList model has a field for book ids. It shouldn't. Your BookWishListCustomization model's book id field and wishlist id field along with the belongs_to and has_many associations your models have now are enough.

find_or_create_by_name with has_many :through

Artists have many Events. Events have many Artists. The join between these two models is called Performances.
I'm trying to associate Artists with Events on the Event add/edit page. I would like to be able to add an Artist only if it doesn't exist, and create the join (performance) regardless. An Artist should be associated with an Event only once.
It was suggested that I use find_or_create_by_name instead of accepts_nested_attributes_for.
I'm following the Railscasts #102 instructions for Auto-Complete which say to use virtual attributes. I haven't even gotten to the auto-complete part, just trying to get find_or_create_by_name working.
I'm getting "undefined method `artist_name' for #" on the Event edit and new pages. In the Railscast, Ryan gets an undefined method before he adds the methods to the model. But I have the method in the Model.
No idea what to do.
event.rb
validates_presence_of :name, :location
validates_uniqueness_of :name
validates_associated :performances
has_many :performances, :dependent => :delete_all
has_many :artists, :through => :performances
#accepts_nested_attributes_for :artists, :reject_if => proc {|a| a['name'].blank?}, :allow_destroy => true
def artist_name
artist.name if artist
end
def artist_name=(name)
self.artist = Artist.find_by_name(name) unless name.blank?
end
artist.rb
validates_presence_of :name
has_many :mixes
has_many :performances, :dependent => :delete_all
has_many :events, :through => :performances
perfomance.rb
belongs_to :artist
belongs_to :event
events_controller.rb
def create
#event = Event.new(params[:event])
respond_to do |format|
if #event.save
flash[:notice] = 'Event was successfully created.'
format.html { redirect_to(admin_events_url) }
format.xml { render :xml => #event, :status => :created, :location => #event }
else
format.html { render :action => "new" }
format.xml { render :xml => #event.errors, :status => :unprocessable_entity }
end
end
end
_form.html.erb
<% form_for([:admin,#event]) do |f| %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<p>
<%= f.label :location %><br/>
<%= f.text_field :location %>
</p>
<p>
<%= f.label :date %><br />
<%= f.date_select :date %>
</p>
<p>
<%= f.label :description %><br />
<%= f.text_area :description %>
</p>
<% f.fields_for :artists do |builder| %>
<%= render 'artist_fields', :f => builder %>
<% end %>
<p><%= link_to_add_fields "Add Artist", f, :artists %></p>
<p>
<%= f.submit 'Submit' %> <%= link_to 'Cancel', admin_events_path %>
</p>
<% end %>
_artist_fields.html.erb
<p class="fields">
<%= f.label :artist_name, "Artist"%><br/>
<%= f.text_field :artist_name %>
<%= link_to_remove_fields "remove", f %>
</p>
Personally I would go back to accepts_nested_attributes_for, ryan bates method there was in the days before nested attributes.
In your controller do something like:
def new
#event = Event.find params[:id]
#artist = #event.artists.build
def edit
#event = Event.find params[:event_id]
#artist = #event.artists.find params[:user_id]
While in the view
...
<% f.fields_for :artists, #artist do |builder| %>
...

Resources